//App
window.App = {
    debug: false,
    size: '',
    mainContainer: $('#main-container'),
    titleContainer: $('.page-title'),
    bootstrap: function(menu){
        var self = this;
        //禁用浏览器右键菜单
        $(document).bind("contextmenu", function(){return false;});
        //弹窗z-index计数器
        this.winIndexCounter = new App.Counter(2, 1000);
        //用户信息
        this.user.init();
        //权限
        this.permission.init(function(){
            //创建主菜单
            App.menu.init(menu);
            //导航初始化
            App.nav.init();
        });
        //页面点击栈初始化
        this.domClickStack.init();
        //窗口初始化
        $(window).resize(function () {
            self.resizeWindow();
        });
        self.resizeWindow();
        //初始化图片预览器
        this.imgViewer.init();
        //Fastclick
        if(this.size === 'sm'){
            FastClick.attach(document.body);
        }
    },
    resizeWindow: function(){
        if(Modernizr.mq('(max-width: 767px)')){//小
            this.size = 'sm';
            if($('#wrapper').hasClass('md')){
                $('#wrapper').removeClass('md');
            }
            $('#wrapper').addClass('sm');
            $('#wrapper').removeClass('sidebar-mini');
        } else if(Modernizr.mq('(min-width: 768px)') && Modernizr.mq('(max-width: 868px)')) {//中
            this.size = 'md';
            if($('#wrapper').hasClass('sm')){
                $('#wrapper').removeClass('sm');
            }
            $('#wrapper').addClass('md');
            $('#wrapper').addClass('sidebar-mini');
            $('.main-menu').find('.openable').removeClass('open');
            $('.main-menu').find('.submenu').removeAttr('style');
        } else {//大
            this.size = 'lg';
            if($('#wrapper').hasClass('md')){
                $('#wrapper').removeClass('md');
            }
            if($('#wrapper').hasClass('sm')){
                $('#wrapper').removeClass('sm');
            }
            if($('#wrapper').hasClass('sidebar-mini')) {
                $('#wrapper').removeClass('sidebar-mini');
                $('.main-menu').find('.openable').removeClass('open');
                $('.main-menu').find('.submenu').removeAttr('style');
            }
        }
        this.resizeContainer();
    },
    resizeContainer: function(){
        var menuWidth = $('aside').width();
        if($('#wrapper').hasClass('sidebar-hide')){
            menuWidth = 0;
        }
        if(!menuWidth){
            $('#content').css({width: '100%'});
        }else{
            $('#content').attr('style', 'width: calc( 100% - '+menuWidth+'px )');
        }
    }
};
//观察者
App.Listeners = function(publisher){
    this.publisher = publisher;
    this.stack = [];
};
App.Listeners.prototype = {
    constructor: App.Listeners,
    add: function(name, callback){
        this.stack.push({
            name: name,
            callback: callback
        });
    },
    remove: function(name){
        for(var i in this.stack){
            if(this.stack[i].name === name){
                this.stack.splice(i, 0);
                break;
            }
        }
    },
    fire: function(){
        for(var i in this.stack){
            var item = this.stack[i];
            item.callback.apply(this.publisher, arguments);
        }
    }
};
//计数器
App.Counter = function(step, origin){
    this.origin = origin || 0;
    this.step = step || 1;
    this.reset();
};
App.Counter.prototype = {
    constructor: App.Counter,
    get: function(uninc){
        if(!uninc){
            this.count = this.count + this.step;
        }
        return this.count;
    },
    reset: function(){
        this.count = this.origin;
    }
};
//栈
App.Stack = function(){
    this.stack = [];
};
App.Stack.prototype = {
    constructor: App.Stack,
    add: function(item, name){
        this.stack.push({
            name: name,
            item: item
        });
    },
    get: function(name){
        for(var i in this.stack){
            if(this.stack[i].name === name){
                return this.stack[i].item ;
            }
        }
    },
    all: function(){
        return this.stack;
    },
    each: function(callback){
        var items = this.stack;
        for(var i in items){
            var item = items[i];
            callback(item.item, item.name);
        }
    },
    count: function(){
        return this.stack.length;
    },
    remove: function(name){
        for(var i in this.stack){
            if(this.stack[i].name === name){
                this.stack.splice(i, 1);
                break;
            }
        }
    }
};
//页面点击栈
App.domClickStack = {
    stack: null,
    bind: false,
    init: function(){
        var self = this;
        this.stack = new App.Stack();
        $('body').on('click', function(e){
            self.stack.each(function(callback){
                callback(e);
            });
        });
    },
    add: function(callback, name){
        this.stack.add(callback, name);
        //this.flush();
    },
    remove: function(name){
        this.stack.remove(name);
        //this.flush();
    },
    flush: function(){
        if(this.stack.count() > 0 && this.bind === false){
            var self = this;
            $('body').click(function(e){
                self.stack.each(function(callback){
                    callback(e);
                });
            });
            self.bind = true;
        }else if(this.bind){
            $('body').unbind('click');
            this.bind = false;
        }
    }
};
//加载
App.Loading = function(conf){
    this.config = conf;
    this.frame = null;
    this.container = null;
    this.isLoading = false;
    this.style = '';
    this.init();
};
App.Loading.prototype = {
    constructor: App.Loading,
    init: function(){
        this.container = this.config.container || App.mainContainer;
        this.frame = this.getFrame();
        this.frame.appendTo(this.container);
    },
    getFrame: function(){
        return $([
            '<div class="loading">',
            '   <div class="overfloat">',
            '   </div>',
            '   <div class="loading-block">',
            '       <div class="overfloat">',
            '       </div>',
            '       <div class="icon">',
            '            <div class="lds-css ng-scope">',
            '                <div class="lds-spinner" style="100%;height:100%">',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '                <div></div>',
            '            </div>',
            '       </div>',
            '   </div>',
            '</div>'
        ].join(''));
    },
    start: function(){
        if(this.isLoading){
            return ;
        }
        this.style = this.container.attr('style') || '';
        var containerStyle = this.style ? this.style+';overflow: hidden;' : 'overflow: hidden;';
        this.container.attr('style', containerStyle);
        var top = this.container.scrollTop();
        var left = this.container.scrollLeft();
        var style = 'display: block;';
        if(top){
            style += 'top:'+top+'px;';
        }
        if(left){
            style += 'top:'+left+'px;';
        }
        this.frame.attr('style', 'display: block;top:'+top+'px;left:'+left+'px');
        this.isLoading = true;
    },
    end: function(){
        this.frame.hide();
        this.container.attr('style', this.style);
        this.isLoading = false;
    },
    destroy: function(){
        this.frame.remove();
    }
};
//工具集
App.util = {
    extend: null,
    hint: null,
    request: null,
    validator: null,
    formator: null,
    base64: null,
    uploader: null
};
//log
App.util.log = function(msg) {
    console.log(msg);
};
//继承
App.util.extend = function(Child, Parent) {
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.super = Parent.prototype;
};
//复制
App.util.copy = function(target, deep) {
    var cloneObj = function(obj){
        var newObj = {};  
        if(obj instanceof Array) {  
            newObj = [];  
        }
        for(var key in obj) {
            var val = obj[key];
            if(deep){
                if(val === null){
                    newObj[key] = null;
                }else if(typeof val === 'object'){
                    newObj[key] = arguments.callee(val);
                }else{
                    newObj[key] = val;
                }
            }else{
                newObj[key] = val;
            }
        }
        return newObj;
    };
    if(typeof target === 'object'){
        return cloneObj(target);
    }else{
        return target;
    }
};
//base64编码
App.util.base64 = {
    base64EncodeChars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    base64DecodeChars: [
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
        -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
    ],
    encode: function(str){
        var out, i, len;
        var c1, c2, c3;
        len = str.length;
        i = 0;
        out = "";
        while(i < len) {
            c1 = str.charCodeAt(i++) & 0xff;
            if(i == len){
                out += this.base64EncodeChars.charAt(c1 >> 2);
                out += this.base64EncodeChars.charAt((c1 & 0x3) << 4);
                out += "==";
                break;
            }
            c2 = str.charCodeAt(i++);
            if(i == len){
                out += this.base64EncodeChars.charAt(c1 >> 2);
                out += this.base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
                out += this.base64EncodeChars.charAt((c2 & 0xF) << 2);
                out += "=";
                break;
            }
            c3 = str.charCodeAt(i++);
            out += this.base64EncodeChars.charAt(c1 >> 2);
            out += this.base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
            out += this.base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
            out += this.base64EncodeChars.charAt(c3 & 0x3F);
        }
        return out;
    },
    decode: function(str){
        var c1, c2, c3, c4;
        var i, len, out;

        len = str.length;
        i = 0;
        out = "";
        while(i < len) {
            /* c1 */
            do {
                c1 = this.base64DecodeChars[str.charCodeAt(i++) & 0xff];
            } while(i < len && c1 == -1);
            if(c1 == -1)
                break;

            /* c2 */
            do {
                c2 = this.base64DecodeChars[str.charCodeAt(i++) & 0xff];
            } while(i < len && c2 == -1);
            if(c2 == -1)
                break;

            out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
            /* c3 */
            do {
                c3 = str.charCodeAt(i++) & 0xff;
                if(c3 == 61)
                return out;
                c3 = this.base64DecodeChars[c3];
            } while(i < len && c3 == -1);
            if(c3 == -1)
                break;

            out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));

            /* c4 */
            do {
                c4 = str.charCodeAt(i++) & 0xff;
                if(c4 == 61)
                return out;
                c4 = this.base64DecodeChars[c4];
            } while(i < len && c4 == -1);
            if(c4 == -1)
                break;
            out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
        }
        return out;
    }
};
//命名空间
App.util.ns = function(path) {
    var arr = path.split(".");    
    var ns = "";    
    for(var i=0;i<arr.length;i++){    
        if(i>0) ns += ".";    
        ns += arr[i];    
        eval("if(typeof(" + ns + ") == 'undefined') " + ns + " = new Object();");    
    }
};
//命名空间
App.util.filter = {
    except: function(data, conditions){
        var res = [];
        var item = null;
        for(var i in data){
            item = data[i];
            if(!this.or(item, conditions)){
                res.push(item);
            }
        }
        return res;
    },
    only: function(data, conditions){
        var res = [];
        var item = null;
        for(var i in data){
            item = data[i];
            if(this.or(data[i], conditions)){
                res.push(item);
            }
        }
        return res;
    },
    and: function(item, conditions){
        if(!conditions || conditions.length < 1){
            return true;
        }
        var res = true;
        for(var i in conditions){
            var condition = conditions[i];
            if(typeof condition === 'function'){
                if(!condition(item)){
                    res = false;
                    continue;
                }
            }
            if(condition.length === 2){
                condition = [condition[0], '=', condition[1]];
            }else if(condition.length !== 3){
                res = false;
                continue;
            }
            var field = condition[0];
            var operator = condition[1]+'';
            var expected = condition[2]+'';
            var value = item[field]+'';
            if(value === undefined){
                res = false;
                continue;
            }
            switch (operator){
                case '=' :
                    if(value !== expected){
                        res = false;
                    }
                    break;
                case '>' :
                    if(value <= expected){
                        res = false;
                    }
                    break;
                case '<' :
                    if(value >= expected){
                        res = false;
                    }
                    break;
                case '!=' :
                    if(value === expected){
                        res = false;
                    }
                    break;
                case 'in' :
                    if(typeof expected === 'string'){
                        expected = expected.split(',');
                    }
                    if(expected.indexOf(value) === false){
                        res = false;
                    }
                    break;
                default:
                    res = false;
            }
        }
        return res;
    },
    or: function(item, conditions){
        if(!conditions || conditions.length < 1){
            return true;
        }
        var res = false;
        for(var i in conditions){
            var condition = conditions[i];
            if(typeof condition === 'function'){
                if(condition(item)){
                    res = true;
                    continue;
                }
            }
            if(condition.length === 2){
                condition = [condition[0], '=', condition[1]];
            }else if(condition.length !== 3){
                continue;
            }
            var field = condition[0];
            var operator = condition[1]+'';
            var expected = condition[2]+'';
            var value = item[field]+'';
            if(value === undefined){
                continue;
            }
            switch (operator){
                case '=' :
                    if(value === expected){
                        res = true;
                    }
                    break;
                case '>' :
                    if(value > expected){
                        res = true;
                    }
                    break;
                case '<' :
                    if(value < expected){
                        res = true;
                    }
                    break;
                case '!=' :
                    if(value !== expected){
                        res = true;
                    }
                    break;
                case 'in' :
                    if(typeof expected === 'string'){
                        expected = expected.split(',');
                    }
                    if(expected.indexOf(value) > -1){
                        res = true;
                    }
                    break;
            }
        }
        return res;
    }
};
//格式化
App.util.formator = {
    date: function(timestamp, defaultDate){
        defaultDate = defaultDate || '';
        if(!timestamp && defaultDate !== 'now'){
            return defaultDate;
        }
        var time = timestamp ? new Date(parseInt(timestamp) * 1000) : new Date();
        var year = time.getFullYear();
        var month = time.getMonth()+1;
        var day = time.getDate();
        var formatNumber = function(number){
            return (number > 9 ? number : ('0' + number));
        };
        return year + "-" + formatNumber(month) + "-" + formatNumber(day);
    },
    dateTime: function(timestamp, defaultDate){
        defaultDate = defaultDate || '';
        if(!timestamp && defaultDate !== 'now'){
            return defaultDate;
        }
        var now = timestamp ? new Date(parseInt(timestamp) * 1000) : new Date();
        var year = now.getFullYear();
        var month = now.getMonth()+1;
        var day = now.getDate();
        var hour = now.getHours();
        var minute = now.getMinutes();
        var second = now.getSeconds();
        var formatNumber = function(number){
            return (number > 9 ? number : ('0' + number));
        };
        return year + "-" + formatNumber(month) + "-" + formatNumber(day) + " " + formatNumber(hour) + ":" + formatNumber(minute) + ":" + formatNumber(second);
    },
    timestamp: function(datetime){
        datetime = datetime+'';
        if(datetime.length === 4){
            datetime = datetime + '-01-01 00:00:00';
        }else if(datetime.length === 7){
            datetime = datetime + '-01 00:00:00';
        }else if(datetime.length === 10){
            datetime = datetime + ' 00:00:00';
        }else if(datetime.length === 13){
            datetime = datetime + ':00:00';
        }else if(datetime.length === 16){
            datetime = datetime + ':00';
        }
        var date = datetime ? new Date(datetime) : new Date();
        return parseInt(date.getTime() / 1000);
    },
    time: function(seconds){
        seconds = parseInt(seconds);
        if(seconds >= 3600){
            var hours = Math.floor(seconds / 3600) + '';
            var minSec = seconds % 3600;
            var mins = Math.floor(minSec / 60) + '';
            var secs = (minSec % 60) + '';
            return (hours.length < 2 ? '0'+hours : hours) + ':' + (mins.length < 2 ? '0'+mins : mins) + ':' + (secs.length < 2 ? '0'+secs : secs);
        }else if(seconds >= 60){
            var mins = Math.floor(seconds / 60) + '';
            var secs = (seconds % 60) + '';
            return '00:' +(mins.length < 2 ? '0'+mins : mins) + ':' + (secs.length < 2 ? '0'+secs : secs);
        }else{
            var secs = seconds+'';
            return '00:00:' + (secs.length < 2 ? '0'+secs : secs);
        }
    },
    timeToSeconds: function(timeStr){
        var arr = timeStr.split(':');
        if(arr.length > 2){
            return parseInt(arr[0])*3600 + parseInt(arr[1])*60 + parseInt(arr[2]);
        }else if(arr.length > 1){
            return parseInt(arr[0])*60 + parseInt(arr[1]);
        }else{
            return parseInt(arr[0]);
        }
    },
    filesize: function(size){
        var K = 1024;
        var M = 1024*1024;
        var G = 1024*1024*1024;
        size = parseInt(size);
        if(size >= G){
            return (size / G).toFixed(2) + 'G';
        }else if(size >= M){
            return (size / M).toFixed(2) + 'M';
        }else{
            return (size / K).toFixed(2) + 'K';
        }
    },
    fileUrl: function(file, defaultFile){
        file = file || '';
        defaultFile = defaultFile || '/img/logo.png';
        var qiniuUrl = 'http://img.molihop.com/';
        var fileUrl = '';
        if(!file){//地址不存在
            if(defaultFile.indexOf("http://") === 0){//默认地址为绝对地址
                fileUrl = defaultFile;
            }else if(defaultFile.indexOf("/") === -1){//默认地址为七牛地址
                fileUrl = qiniuUrl + defaultFile;
            }else{//默认地址为相对地址
                fileUrl = defaultFile;
            }
        }else if(file.indexOf("http://") === 0){//绝对地址
            fileUrl = file;
        }else{
            if(file.indexOf("/") === -1){//七牛地址
                fileUrl = qiniuUrl + file;
            }else{//相对地址
                fileUrl = file;
            }
        }
        return fileUrl;
    }
};
//提示
App.util.hint = {
    alert: function(config){//警告框
        var text = config.text || '';
        var ensureText = config.ensureText || '确定';
        var ensure = config.ensure || config.yes || function(){};
        var frame = $([
            '<div class="custom-popup" style="min-width: 200px;">',
            '    <div class="padding-md">',
            '        <h4 class="m-top-none">'+text+'</h4>',
            '    </div>',
            '    <div class="text-center">',
            '        <a class="ensure-btn btn btn-danger" href="javascript:void(0);">'+ensureText+'</a>',
            '    </div>',
            '</div>'
        ].join(''));
        frame.find('.ensure-btn').click(function(){
            frame.popup('hide');
            frame.remove();
            frame = null;
            ensure();
        });
        frame.appendTo($('body'));
        frame.popup('show');
    },
    confirm: function(config){//确认框
        var text = config.text || '';
        var ensureText = config.ensureText || config.yesText || '确定';
        var cancelText = config.cancelText || config.noText || '取消';
        var ensure = config.ensure || config.yes || function(){};
        var cancel = config.cancel || config.no || function(){};
        var frame = $([
            '<div class="custom-popup" style="min-width: 250px;">',
            '    <div class="padding-md">',
            '        <h4 class="m-top-none">'+text+'</h4>',
            '    </div>',
            '    <div class="text-center">',
            '        <a class="ensure-btn btn btn-success m-right-sm" href="javascript:void(0);">'+ensureText+'</a>',
            '        <a class="cancel-btn btn btn-danger" href="javascript:void(0);">'+cancelText+'</a>',
            '    </div>',
            '</div>'
        ].join(''));
        frame.find('.ensure-btn').click(function(){
            frame.popup('hide');
            ensure();
        });
        frame.find('.cancel-btn').click(function(){
            frame.popup('hide');
            frame.remove();
            frame = null;
            cancel();
        });
        frame.appendTo($('body'));
        frame.popup('show');
    },
    toast: function(config){//提示
        return {
            config: null,
            closed: false,
            locked: false,
            container: $('#main-container'),
            init: function(config){
                this.frame();
                this.config = config;
                var self = this;
                //类型
                switch (config.type){
                    case 'ok' :
                    case 'success' :
                        this.frame.addClass('alert-success');
                        this.frame.find('.icon').html('<i class="fa fa-check-circle"></i>');
                        break;
                    case 'info' :
                        this.frame.addClass('alert-info');
                        this.frame.find('.icon').html('<i class="fa fa-info-circle"></i>');
                        break;
                    case 'warning' :
                        this.frame.addClass('alert-warning');
                        this.frame.find('.icon').html('<i class="fa  fa-warning"></i>');
                        break;
                    case 'error' :
                    case 'danger' :
                        this.frame.addClass('alert-danger');
                        this.frame.find('.icon').html('<i class="fa fa-times-circle"></i>');
                        break;
                    case 'load' :
                    case 'flush' :
                    case 'refresh' :
                    case 'loading' :
                        this.frame.addClass('alert-warning');
                        this.frame.find('.icon').html('<i class="fa fa-circle-o-notch fa-spin"></i>');
                        break;
                }
                //添加文字
                if(config.text){
                    this.frame.find('.text').text(config.text);
                }
                //添加HTML
                if(config.html){
                    this.frame.find('.html').html(config.html);
                }
                //关闭按钮
                this.frame.find('button.close').click(function(){
                    self.close();
                });
                //显示
                this.frame.appendTo(self.container);
                this.frame.fadeIn('fast');
                //加锁
                if(config.lock){
                    this.lock();
                }
                return this;
            },
            frame: function(){
                var self = this;
                var frame = $([
                    '<div class="alert" style="position: fixed;width:250px;z-index:10001;padding: 8px;line-height:16px;margin-bottom:0;">',
                    '    <button type="button" class="close" style="font-size:16px;">',
                    '        <span aria-hidden="true">&times;</span>',
                    '    </button>',
                    '    <span class="icon" style="margin-right:6px;"></span>',
                    '    <span class="text"></span>',
                    '    <span class="html"></span>',
                    '</div>'
                ].join(''));
                frame.css({
                    top: parseInt($(window).height()/2+45-18)+'px',
                    left: parseInt(self.container.offset().left+self.container.outerWidth()/2-125)+'px'
                });
                this.frame = frame;
                return this;
            },
            delay: function(time, callback){//延时关闭
                var self = this;
                setTimeout(function(){
                    self.close();
                    if(callback){
                        callback();
                    }
                }, parseInt(time));
                return this;
            },
            destroy: function(){
                this.close();
            },
            close: function(){
                if(this.closed){
                    return ;
                }
                var self = this;
                this.frame.fadeOut("fast", function(){
                    self.frame.remove();
                    self.frame = null;
                });
                this.unlock();
                this.closed = true;
                return this;
            },
            lock: function(){
                this.lockLayer = $('<div class="overfloat"></div>');
                this.lockLayer.appendTo(this.container);
                this.lockLayer.show();
                this.locked = true;
            },
            unlock: function(){
                if(this.locked){
                    this.lockLayer.remove();
                    this.locked = false;
                }
            }
        }.init(config);
    }
};
//请求
App.util.request = {
    getData: function(config){
        var conf = this.config(config);
        $.ajax({
            url: conf.url,
            data: conf.data,
            type: 'GET',
            async: conf.async,
            beforeSend: function(){
                if(conf.loading){
                    conf.loading.start();
                }
                conf.before();
            },
            success: function(returnData){
                if(conf.loading){
                    conf.loading.end();
                }
                var dataRaw = JSON.parse(returnData);
                if(dataRaw.status+'' === '0'){
                    conf.success(dataRaw.data);
                }else{
                    conf.error(dataRaw.msg, dataRaw.status);
                }
            },
            error: function(){
                if(conf.loading){
                    conf.loading.end();
                }
                conf.error('网络请求失败');
            }
        });
    },
    postData: function(config){
        var conf = this.config(config);
        $.ajax({
            url: conf.url,
            data: conf.data,
            type: "POST",
            async: conf.async,
            beforeSend: function(){
                if(conf.loading){
                    conf.loading.start();
                }
                conf.before();
            },
            success: function(returnData){
                if(conf.loading){
                    conf.loading.end();
                }
                var dataRaw = JSON.parse(returnData);
                if(dataRaw.status === '0'){//操作成功
                    conf.success(dataRaw.data);
                }else{//操作失败
                    conf.error(dataRaw.msg, dataRaw.status);
                }
            },
            error: function(){
                if(conf.loading){
                    conf.loading.end();
                }
                conf.error('网络请求失败');
            }
        });
    },
    config: function(config){//解析配置参数
        return {
            url: config.url,
            data: config.data || {},
            before: config.before || function(){},
            success: config.success || function(){},
            error: function(msg, status){
                if(status+'' === '-1'){//未登录
                    return App.util.hint.toast({
                        type: 'error',
                        text: msg
                    }).delay(1000, function(){
                        window.location.href = '/login'; 
                    });
                }else if(status+'' === '-2'){//越权
                    return App.util.hint.toast({
                        type: 'error',
                        text: msg
                    }).delay(2000);
                }
                if(config.error){
                    config.error(msg, status);
                }else{
                    App.util.hint.toast({
                        type: (status+'' === '1' ? 'warning' : 'error'),
                        text: msg
                    }).delay(3000);
                }
            },
            async: config.async === undefined ? true : config.async,
            loading: config.loading instanceof App.Loading ? config.loading : false,
            loadingText: config.loadingText || ''
        };
    }
};
//上传
App.util.uploader = {
    token: null,
    uploadDriver: null,
    tokenUrl: null,
    uploadUrl: null,
    lastGetTokenTime: null,
    tokenExpires: 3600*1000,
    init: function(){
        //设置驱动类型
        this.uploadDriver = 'qiniu';
        if(this.uploadDriver === 'qiniu'){
            this.tokenUrl = '/getQiniuUploadToken';
            this.uploadUrl = 'http://up-z1.qiniu.com';
        }else if(this.uploadDriver === 'local'){
            this.tokenUrl = '/getLocalUploadToken';
            this.uploadUrl = 'http://upload.onairm.com/upload.php';
        }
        return this;
    },
    //获取上传地址
    getUploadUrl : function(){
        return this.uploadUrl;
    },
    //获取唯一键
    getKey : function(){
        var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""), uuid = [], i;
        var r;
        // rfc4122 requires these characters
        uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-";
        uuid[14] = "4";
        // Fill in random data.  At i==19 set the high bits of clock sequence as
        // per rfc4122， sec. 4.1.5
        for (i = 0; i < 36; i++) {
            if (!uuid[i]) {
                r = 0 | Math.random()*16;
                uuid[i] = chars[(i === 19) ? (r & 0x3) | 0x8 : r];
            }
        }
        return this.getMd5(uuid.join(""));
    },
    //获取上传令牌
    getToken: function(process){
        if(!this.token || this.lastGetTokenTime + this.tokenExpires < this.getTimestamp()){
            this.flushToken();
        }
        return this.token;
    },
    //刷新上传令牌
    flushToken: function(){
        var self = this;
        App.util.request.getData({
            url : self.tokenUrl,
            async: false,
            loading: false,
            success: function(data){
                self.token = data;
                self.lastGetTokenTime = self.getTimestamp();
            }
        });
        return this;
    },
    //获取当前时间戳
    getTimestamp: function(){
        return Date.parse(new Date());
    },
    //调整图片位置
    resizeImage : function(imgObj, width, height){
        var resize = function(){
            var url = imgObj.attr("src") ;
            var img = new Image(); //创建一个Image对象，实现图片的预下载
            img.src = url;
            var w = img.width;
            var h = img.height;
            var margin_left = 0;
            var margin_top = 0;
            if(w <= width && h <= height){//小图，完全在容器里面
                margin_left = parseInt((width - w)/2);
                margin_top = parseInt((height - h)/2);
            }else{
                var w_proportion = w/width;
                var h_proportion = h/height;
                if(w_proportion > h_proportion){//宽图
                    margin_left = 0;
                    margin_top = parseInt((height - h*width/w)/2);
                }else if(w_proportion < h_proportion){//高图
                    margin_left = parseInt((width - w*height/h)/2);
                    margin_top = 0;
                }else{//正好
                    margin_left = 0;
                    margin_top = 0;
                }
            }
            imgObj.css('margin-left', margin_left+'px').css('margin-top', margin_top);
        };
        var url = imgObj.attr("src") ;
        var img = new Image(); //创建一个Image对象，实现图片的预下载
        img.src = url;
        if(img.complete) { // 如果图片已经存在于浏览器缓存，直接调用回调函数
            resize();
            return; // 直接返回，不用再处理onload事件
        }
        img.onload = function(){ //图片下载完毕时异步调用callback函数。
            resize();//将回调函数的this替换为Image对象
        };
    },
    //获取音视频信息
    getAvinfo : function(key, callback, error){
        $.ajax({
            url: App.util.formator.fileUrl(key)+'?avinfo',
            type: "GET",
            async: true,
            success: function(returnData){
                var streamsRaw = returnData.streams ? returnData.streams : [];
                var streams = {};
                for(var i in streamsRaw){
                    if(streamsRaw[i].codec_type){
                        streams[streamsRaw[i].codec_type] = streamsRaw[i];
                    }
                }
                var format = returnData.format ? returnData.format : {};
                callback({
                    format: format,
                    streams: streams
                });
            },
            error: function(){
                if(error){
                    error();
                }
            }
        });
    },
    //获取文件MD5
    getMd5 : function(inputStr){
        var spark = new SparkMD5();
        spark.append(inputStr);
        return spark.end();
    },
    //获取文件MD5
    getFileMd5 : function(file, success, error, process){
        var self = this,
            chunkSize = 2097152,                           // read in chunks of 2MB
            chunks = Math.ceil(file.size / chunkSize),
            currentChunk = 0,
            spark = new SparkMD5(),
            fileReader = new FileReader(),
            loadNext = function(){
                var start = currentChunk * chunkSize,
                    end = start + chunkSize >= file.size ? file.size : start + chunkSize;
                fileReader.readAsBinaryString(self.sliceFile(file, start, end));
                if(process){
                    process(parseInt(currentChunk*100/chunks));
                }
            };
        fileReader.onload = function (e) {
            spark.appendBinary(e.target.result);                 // append array buffer
            currentChunk += 1;
            if (currentChunk < chunks) {
                loadNext();
            } else {
                success(spark.end());
            }
        };
        fileReader.onerror = function () {
            error();
        };
        loadNext();
    },
    upload: function(config){
        return {
            config: null,
            file: null,
            key: null,
            blockSize: 4*1024*1024,
            fileSize: null,
            blocks: [],
            blockCount: null,
            percent: 0,
            speed: 0,
            concurrentCount: 4,
            errorTimes: 0,
            alowedErrorTimes: 3,
            status: 0,/* 0 待上传 1正在上传 2分片上传完成，待合并 3上传出错 4上传成功 5暂停上传 6销毁*/
            init: function(config){
                var self = this;
                this.config = config;
                this.file = config.file;
                this.fileSize = this.file.size;
                this.blockCount = Math.ceil(this.fileSize / this.blockSize);
                this.tick(function(){
                    if(self.config.progress){
                        self.config.progress(self.percent, self.speed);
                    }
                });
                return this;
            },
            start: function(){
                this.status = 1;
                var blockCount = this.blocks.length;
                var uploadedCount = 0;
                var uploadingCount = 0;
                var blockCountLeft = 0;
                for(var i in this.blocks){
                    if(this.blocks[i].status === 1){
                        uploadedCount += 1;
                    }
                }
                uploadingCount = blockCount - uploadedCount;
                blockCountLeft = this.blockCount - uploadedCount;
                var count = blockCountLeft > (this.concurrentCount - uploadingCount) ? (this.concurrentCount - uploadingCount) : blockCountLeft;
                for(var i=0; i<count; i++){
                    this.blocks.push(this.getBlock(i+blockCount).upload());
                }
                //触发进度
                this.tick.start();
                return this;
            },
            pause: function(){
                this.status = 5;
                return this;
            },
            destroy: function(){
                this.status = 6;
                this.file = null;
                for(var i in this.blocks){
                    this.blocks[i].destroy();
                }
                this.blocks = [];
                return this;
            },
            complete: function(){
                var self = this;
                this.status = 2;
                var ctxsArr = [];
                for(var i=0; i<this.blockCount; i++){
                    for(var j in this.blocks){
                        if(this.blocks[j]['offset'] === i){
                            ctxsArr.push(this.blocks[j]['ctx']);
                        }
                    }
                }
                var ctxs = ctxsArr.join(',');
                var xhr = new XMLHttpRequest();
                var uploader = App.util.uploader;
                var key = self.config.key ? self.config.key(uploader.getKey()) : uploader.getKey();
                xhr.open('POST', uploader.getUploadUrl()+'/mkfile/'+self.fileSize+'/key/'+encodeURI(App.util.base64.encode(key)), true);
                xhr.setRequestHeader("Content-Type", 'text/plain');
                xhr.setRequestHeader("Authorization", 'UpToken '+uploader.getToken());
                //上传成功
                xhr.onreadystatechange = function(){
                    if(xhr.readyState === 4){
                        if(xhr.status !== 200){
                            self.errorTimes += 1;
                            if(self.errorTimes <= self.alowedErrorTimes){
                                setTimeout(function(){
                                    self.complete();
                                }, 1000);
                            }else{
                                self.error();
                            }
                            return ;
                        }
                        self.success(JSON.parse(xhr.responseText));
                    }
                };
                try{
                    xhr.send(ctxs);
                }catch(err){
                    self.errorTimes += 1;
                    if(self.errorTimes <= self.alowedErrorTimes){
                        setTimeout(function(){
                            self.complete();
                        }, 1000);
                    }else{
                        self.error();
                    }
                }
                return this;
            },
            error: function(){
                this.status = 3;
                if(this.config.error){
                    this.config.error();
                }
            },
            success: function(responseData){
                this.status = 4;
                this.key = responseData.data.key;
                this.avinfo = responseData.data.avinfo;
                if(this.config.success){
                    this.config.success(this.key);
                }
            },
            tick: function(callback){
                var self = this;
                this.tick = {
                    timer: null,
                    lastLoaded: 0,
                    operation: function(){
                        var uploaded = 0;
                        for(var i in self.blocks){
                            uploaded += self.blocks[i].loaded;
                        }
                        //百分比
                        self.percent = Math.floor(uploaded / self.fileSize * 100);
                        //网速
                        self.speed = this.lastLoaded ? (uploaded - this.lastLoaded) : 0;
                        this.lastLoaded = uploaded;
                        callback();
                        //状态改变
                        if(self.status !== 1){
                            this.stop();
                        }
                    },
                    start: function(){
                        var self_ = this;
                        this.timer = setInterval(function(){
                            self_.operation();
                        }, 1000);
                    },
                    stop: function(){
                        clearInterval(this.timer);
                    }
                };
                return this;
            },
            flushBlock: function(){
                var completed = true;
                for(var i in this.blocks){
                    if(this.blocks[i].status !== 1){
                        completed = false;
                        continue;
                    }
                }
                if(completed && this.blocks.length === this.blockCount){
                    this.complete();
                    return this;
                }
                if(this.blocks.length < this.blockCount && this.status === 1){
                    this.blocks.push(this.getBlock(this.blocks.length).upload());
                }
                return this;
            },
            getBlock: function(offset){
                var parent = this;
                return {
                    config: null,
                    xhr: null,
                    offset: null,
                    start: 0,
                    end: 0,
                    size: 0,
                    loaded: 0,
                    ctx: null,
                    status: 0,
                    errorTimes: 0,
                    alowedErrorTimes: 3,
                    init: function(offset){
                        this.offset = offset;
                        this.start = parent.blockSize * offset;
                        this.size = parent.blockSize;
                        if(offset+1 === parent.blockCount){
                            this.size = parent.fileSize % parent.blockSize;
                        }
                        this.end = this.start + this.size;
                        this.xhr = new XMLHttpRequest();
                        return this;
                    },
                    upload: function(){
                        var self = this;
                        var uploader = App.util.uploader;
                        this.xhr.open('POST', uploader.getUploadUrl()+'/mkblk/'+this.size, true);
                        this.xhr.setRequestHeader("Content-Type", 'application/octet-stream');
                        this.xhr.setRequestHeader("Authorization", 'UpToken '+uploader.getToken());
                        this.xhr.timeout = 3*60*1000;
                        //进度
                        this.xhr.upload.onprogress = function(evt){
                            if (evt.lengthComputable) {
                                self.loaded = evt.loaded;
                            }
                        };
                        //上传成功
                        this.xhr.onreadystatechange = function(){
                            if(self.xhr.readyState === 4){
                                if(self.xhr.status !== 200){
                                    self.errorTimes += 1;
                                    if(self.errorTimes <= self.alowedErrorTimes){
                                        self.error();
                                    }else{
                                        parent.error();
                                    }
                                    return ;
                                }
                                self.done(JSON.parse(self.xhr.responseText));
                            }
                        };
                        //请求超时
                        this.xhr.onTimeout = function(){
                            self.errorTimes += 1;
                            if(self.errorTimes <= self.alowedErrorTimes){
                                self.error();
                            }else{
                                parent.error();
                            }
                        };
                        //开始上传
                        try{
                            this.xhr.send(uploader.sliceFile(parent.file, self.start, self.end));
                        }catch(err){
                            self.errorTimes += 1;
                            if(self.errorTimes <= self.alowedErrorTimes){
                                self.error();
                            }else{
                                parent.error();
                            }
                        }
                        return this;
                    },
                    error: function(){
                        var self = this;
                        //1秒之后重试
                        if(this.status !== 3){
                            this.percent = 0;
                            this.status = 0;
                            setTimeout(function(){
                                self.upload();
                            }, 1000);
                        }
                    },
                    done: function(response){
                        this.status = 1;
                        this.ctx = response.ctx;
                        this.percent = 100;
                        parent.flushBlock();
                        this.xhr = null;
                    },
                    destroy: function(){
                        this.status = 3;
                        if(this.xhr){
                            this.xhr.abort();
                            this.xhr = null;
                        }
                    }
                }.init(offset);
            }
        }.init(config);
    },
    sliceFile: function(file, startByte, endByte){
        if(file.slice){
            return  file.slice(startByte, endByte);
        }
        // 兼容firefox
        if(file.mozSlice){
            return  file.mozSlice(startByte, endByte);
        }
        // 兼容webkit
        if(file.webkitSlice){
            return  file.webkitSlice(startByte, endByte);
        }
        return null;
    }
}.init();
//字符串
App.util.str = {
    len: function(str){
        var expression = /^[^\u0000-\u024F]+$/;
        var objExp = new RegExp(expression);
        var num = 0;
        for(var i=0; i<str.length;i++){
            if(objExp.test(str[i])){
                num = num + 2;
            }else{
                num = num + 1;
            }
        }
        return num;
    }
};
//验证器
App.util.validator = {
    validate: function(value, rule){
        if(!rule){
            return true;
        }
        switch (rule.type) {
            case 'str' :
            case 'string' :
                var range = rule.range || null;
                return this.str(value, range);
            case 'num' :
            case 'number' :
            case 'numeric' :
                var range = rule.range || null;
                return this.numeric(value, range);
            case 'int' :
            case 'integer' :
                var range = rule.range || null;
                return this.integer(value, range);
            case 'float' :
            case 'decimal' :
                var range = rule.range || null;
                var decimal = rule.decimal || null;
                return this.float(value, range, decimal);
            case 'txt' :
            case 'text' :
                var range = rule.range || null;
                return this.text(value, range);
            case 'html' :
                var range = rule.range || null;
                return this.html(value, range);
            case 'json' :
                var range = rule.range || null;
                var json = rule.json || null;
                var item = rule.item || null;
                return this.json(value, range, json, item);
            case 'in' :
                var item = rule.item || null;
                var in_data = rule.in || [];
                return this.inArray(value, in_data);
            case 'split' :
                var range = rule.range || null;
                var item = rule.item || null;
                return this.split(value, range, item);
            case 'regexp' :
                var pattern = rule.pattern || null;
                return this.regexp(value, pattern);
            default :
                return this[rule.type](value);
        }
    },
    str: function(value, range){
        if(range){
            return this.isInRange(this.strLen(value), range);
        }
        return true;
    },
    numeric: function(value, range){
        if(isNaN(value)){
            return false;
        }
        if(range){
            return this.isInRange((''+value).length, range);
        }
        return true;
    },
    integer: function(value, range){
        if(isNaN(value)){
            return false;
        }
        if(range){
            return this.isInRange(parseInt(value), range);
        }
        return true;
    },
    float: function(value, range, decimal){
        if(isNaN(value)){
            return false;
        }
        if(range && !this.isInRange(parseFloat(value), range)){
            return false;
        }
        if(decimal){
            if((value+'').indexOf('.') < 0){
                return false;
            }
            var decimalStr = (value+'').replace(/.*?\./, '');
            if(!this.isInRange(decimalStr.length, decimal)){
                return false;
            }
        }
        return true;
    },
    text: function (value, range){
        if(range){
            return this.isInRange(this.strLen(value), range);
        }
        return true;
    },
    html: function(value, range){
        if(range){
            return this.isInRange(this.strLen(value), range);
        }
        return true;
    },
    json: function(value, range, json, itemRule){
        var valueArr = JSON.parse(value);
        if(json){
            for(var key in json){
                var rule = json[key];
                if(valueArr[key] === undefined){
                    return false;
                }
                if(!this.validate(valueArr[key], rule)){
                    return false;
                }
            }
        }
        if(itemRule){
            for(var i in valueArr){
                var item = valueArr[i];
                if(!this.validate(item, itemRule)){
                    return false;
                }
            }
        }
        if(range){
            return this.isInRange(valueArr.length, range);
        }
        return true;
    },
    inArray: function(value, in_data){
        if(in_data){
            return in_data.indexOf(value) > -1;;
        }
        return true;
    },
    split: function(value, range, itemRule){
        var valueArr = value.length > 0 ? value.split(',') : [];
        if(itemRule){
            for(var i in valueArr){
                if(!this.validate(valueArr[i], itemRule)){
                    return false;
                }
            }
        }
        if(range){
            return this.isInRange(valueArr.length, range);
        }
        return true;
    },
    regexp: function(value, pattern){
        return (new RegExp(pattern)).test(value);
    },
    phone: function(value){
        return this.regexp(value, '^1\\d{10}$');
    },
    email: function(value){
        return this.regexp(value, '^\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}');
    },
    url: function(value){
        return this.regexp(value, '^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?');
    },
    unique: function(model, field, value, ignore, ignoreField){
        return true;
    },
    isInRange: function(value, range){
        if(range.length === 1){
            range[1] = null;
        }
        if(range[0] && value < range[0]){
            return false;
        }
        if(range[1] && value > range[1]){
            return false;
        }
        return true;
    },
    strLen: function(str){
        return App.util.str.len(str);
    }
};
//复制
App.util.size = function(target) {
    var container = $('#sizeTestTempContainer').first();
    if(!$('#sizeTestTempContainer').length){
        var temp = $('<div id="sizeTestTempContainer" style="overflow: hidden;width:100%;height:100px;top:-120px;position:fixed;"></div>');
        temp.appendTo($('body'));
        container = temp;
    }
    var size = {};
    target.appendTo(container);
    size.width = target.width();
    size.height = target.height();
    return size;
};
App.Win = function(config){
    //参数配置初始化
    this.config = config;
    this.config.width = config.width !== undefined ? parseInt(config.width) : 0;//宽
    this.config.height = config.height !== undefined ? parseInt(config.height) : 0;//高
    this.config.dragAble = config.dragAble !== undefined ? config.dragAble : true;//可拖拽
    this.config.mini = config.mini !== undefined ? config.mini : true;//可折叠
    this.config.lock = config.lock !== undefined ? config.lock : false;//锁定层
    this.config.title = config.title !== undefined ? config.title : '';//标题
    this.config.content = config.content !== undefined ? config.content : '';//内容
    this.config.btns = config.btns !== undefined ? config.btns : [];//按钮
    this.config.url = config.url !== undefined ? config.url : '';//初始化的url
    this.config.callback = config.callback !== undefined ? config.callback : '';//初始化回调
    //私有属性
    this.titleContainer = null;//标题容器
    this.contentContainer = null;//内容容器
    this.btnsContainer = null;//按钮容器
    this.lockLayer = null;//锁定层
    this.draged = false;//是否经过拖拽
    this.margin = 0.1;
    this.contentMaxHeight = 0;
    //初始化
    this.init();
};
App.Win.prototype = {
    constructor: App.Win,
    //初始化组件
    init: function(){
        var self = this;
        //初始化
        this.frame = this.getFrame();
        if(App.size === 'sm' || App.size === 'md'){
            this.margin = 0.05;
        }
        this.flushPosition();
        this.titleContainer = this.frame.find(' > .panel-heading .title');
        this.contentContainer = this.frame.find(' > .panel-body');
        this.btnsContainer = this.frame.find('.panel-footer .btns');
        this.frame.find('.close-btn').click(function(){
            self.hide();
        });
        this.frame.find('.mini-btn').click(function(){
            self.mini();
        });
        this.frame.find('.full-btn').click(function(){
            self.full();
        });
        //层级
        this.toTop();
        //点击自动显示到最上层
        this.autoShowAble();
        //拖拽
        if(this.config.dragAble !== false){     
            this.dragAble();
        }
        //滚动
        //this.scrollAble();
        //折叠
        if(this.config.mini === false){
            this.frame.find('.mini-btn').hide();
        }
        //移动端自动锁定所有弹窗
        if(App.size === 'sm'){
            this.config.lock = true;
        }
        if(this.config.lock){
            this.lockAble();
        }
        //标题
        if(this.config.title){
            this.setTitle(this.config.title);
        }
        //内容
        if(this.config.content){
            this.setContent(this.config.content);
        }
        //按钮
        if(this.config.btns){
            this.setBtns(this.config.btns);
        }
        //回调函数
        if(this.config.callback){
            this.config.callback(this);
        }
        return this;
    },
    fix: function(container){
        this.frame.appendTo(container);
        if(this.lockLayer){
            this.lockLayer.appendTo(container);
        }
    },
    getFrame: function(){
        var frame = $([
            '<div class="panel panel-default pop-window">',
            '    <div class="panel-heading">',
            '        <span class="title">窗口</span>',
            '        <span class="btns">',
            '            <a class="btn btn-sm mini-btn" href="javascript:void(0);"><i class="fa fa-minus-circle fa-lg"></i></a>',
            '            <a class="btn btn-sm full-btn" href="javascript:void(0);"><i class="fa fa-plus-circle fa-lg"></i></a>',
            '            <a class="btn btn-sm close-btn" href="javascript:void(0);"><i class="fa fa-times-circle fa-lg"></i></a>',
            '        </span>',
            '    </div>',
            '    <div class="panel-body">',
            '    </div>',
            '    <div class="panel-footer clearfix">',
            '        <div class="btns pull-right">',
            '        </div>',
            '    </div>',
            '</div>'
        ].join(''));
        frame.find('.full-btn').hide();
        return frame;
    },
    setTitle: function(title){
        //清空内容
        this.titleContainer.html('');
        //设置内容
        if(typeof title === 'string'){
            this.titleContainer.text(title);
        }else{
            this.titleContainer.append(title);
        }
        return this;
    },
    setContent: function(content){
        //清空内容
        this.contentContainer.children().each(function(){
            $(this).remove();
        });
        //设置内容
        if(typeof content === 'string'){
            this.contentContainer.html(content);
        }else{
            this.contentContainer.append(content);
        }
        return this;
    },
    setBtns: function(btns){
        //清空内容
        this.btnsContainer.children().each(function(){
            $(this).remove();
        });
        //设置内容
        for(var i in btns){
            this.btnsContainer.append(btns[i]);
        }
        return this;
    },
    showBtns: function(){
        this.btnsContainer.show();
        return this;
    },
    hideBtns: function(){
        this.btnsContainer.hide();
        return this;
    },
    mini: function(){
        this.frame.find('> .panel-body').hide();
        this.frame.find('> .panel-footer').hide();
        this.frame.find('.mini-btn').hide();
        this.frame.find('.full-btn').show();
        if(this.config.onMini){
            this.config.onMini(this);
        }
        return this;
    },
    full: function(){
        this.frame.find('> .panel-body').show();
        this.frame.find('> .panel-footer').show();
        this.frame.find('.full-btn').hide();
        this.frame.find('.mini-btn').show();
        if(this.config.onFull){
            this.config.onFull(this);
        }
        return this;
    },
    hide: function(){
        this.frame.hide();
        if(this.config.onHide){
            this.config.onHide(this);
        }
        //解锁
        if(this.config.lock){
            this.unlock();
        }
        //取消自动隐藏
        if(this.config.autoHide){
            this.unAutoHideAble();
        }
        //暂停视频
        this.frame.find('video').each(function(){
            $(this)[0].pause();
        });
        return this;
    },
    show: function(){
        //计算位置与大小
        if(!this.draged){
            this.flushPosition();
        }
        //显示窗口
        this.frame.show();
        if(this.config.onShow){
            this.config.onShow(this);
        }
        //显示到顶层
        this.toTop();
        //锁定
        if(this.config.lock){
            this.lock();
        }
        //自动隐藏
        if(this.config.autoHide){
            this.autoHideAble();
        }
        return this;
    },
    lock: function(){
        var self = this;
        this.lockLayer.css({
            width: $(window).width(),
            height: $(window).height(),
            'z-index': self.zIndex - 1
        });
        this.lockLayer.show();
        return this;
    },
    unlock: function(){
        this.lockLayer.hide();
        return this;
    },
    lockAble: function(){
        this.lockLayer = $('<div class="lock-layer"></div>');
    },
    autoHideAble: function(){
        var self = this;
        App.domClickStack.add(function(event){
            var onSelf = self.frame.is(event.target) || self.frame.has(event.target).length > 0;
            if(!onSelf){
                self.hide();
            }
        }, 'win-auto-hide');
    },
    unAutoHideAble: function(){
        App.domClickStack.remove('win-auto-hide');
    },
    dragAble: function(){
        var self = this;
        var frame = this.frame;
        this.frame.find('> .panel-heading').off("mousedown").mousedown(function(e) {
            // 排除非左击
            if(e.which !== 1){
                return; 
            }
            //阻止选中文本
            e.preventDefault();
            //原始数据
            var top = frame.offset().top - $(window).scrollTop();
            var left = frame.offset().left;
            var y = e.pageY;
            var x = e.pageX;
            // 绑定mousemove事件
            var topVal = 0;
            var leftVal = 0;
            var moved = false;
            $(document).mousemove(function(e){
                e.preventDefault();
                // 移动
                topVal = top + (e.pageY - y);
                leftVal = left + (e.pageX - x);
                if(topVal > -1){
                    frame.css('top', topVal);
                }
                if(leftVal > -1){
                    frame.css('left', leftVal);
                }
                moved = true;
            });
            // 绑定mouseup事件
            $(document).mouseup(function() {
                //解除鼠标移动事件
                $(document).off('mouseup').off('mousemove');
                //标记
                if(moved){
                    self.draged = true;
                }
            });
        });
    },
    scrollAble: function(){
        this.frame.find('> .panel-body').scroll(function(e){
            var containerHeight = $(this).find('.container').outerHeight();
            var bottom = $(this).outerHeight()+ $(this).scrollTop();
            var hasUp = $(this).scrollTop() > 0;
            var hasDown = containerHeight >= bottom;
            if(hasUp && hasDown){
                $(this).removeClass('has-up').removeClass('has-down').addClass('has-all');
            }else if(hasUp){
                $(this).removeClass('has-all').removeClass('has-down').addClass('has-up');
            }else if(hasDown){
                $(this).removeClass('has-all').removeClass('has-up').addClass('has-down');
            }
            return false;
        });
    },
    autoShowAble: function(){
        var self = this;
        self.frame.click(function(e){
            if(self.zIndex < App.winIndexCounter.get(true)){
                self.toTop();
            }
        });
    },
    toTop: function(){
        var self = this;
        self.zIndex = App.winIndexCounter.get();
        self.frame.css({'z-index': self.zIndex});
        if(self.lockLayer){
            self.lockLayer.css('z-index', self.zIndex - 1);
        }
    },
    flushPosition: function(){
        var frame = this.frame;
        var margin = this.margin;
        var width = parseInt(this.config.width) || 602;
        var height = parseInt(this.config.height);
        var winHeight = $(window).height();
        var winWidth = $(window).width();
        var containerWidth = App.mainContainer.width();
        var containerHeight = App.mainContainer.height();
        if(winWidth <= width){
            width = winWidth;
        }
        //窗口位置
        frame.css({
            'width': width+'px',
            'height': height ? height+'px' : 'auto',
            'left': parseInt(winWidth - containerWidth + containerWidth/2 - width/2)+'px',
            'top' : parseInt((winHeight - 45)*margin + 45)+'px'
        });
        //内容区高度设置
        this.contentMaxHeight = parseInt((containerHeight)*(1 - margin*2) - 80);
        frame.find('> .panel-body').css({
            'max-height': this.contentMaxHeight+'px'
        });
    },
    exec: function(func){
        var self = this;
        func(self);
    },
    destroy: function(){
        if(this.lockLayer){
            this.lockLayer.remove();
        }
        if(this.config.close){
            this.config.close(this);
        }
        this.frame.remove();
    }
};
//页面
App.Page = function(config){
    this.frame = null;
    this.breadcrumbContainer = null;
    this.contentContainer = null;
    this.name = config.name;
    this.breadcrumb = null;
    this.flash = false;//闪存模式
    this.scrollTop = 0;//页面滚动位置
    this.isDirty = false;//是否已污染
    this.initStack = new App.Listeners(this);//初始化栈
    this.refreshStack = new App.Listeners(this);//刷新栈
    this.showStack = new App.Listeners(this);//显示栈
    this.hideStack = new App.Listeners(this);//隐藏栈
    this.destroyStack = new App.Listeners(this);//销毁栈
    this.wins = [];//窗口
    this.params = null;
    this.inited = false;
};
App.Page.prototype = {
    constructor: App.Page,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="panel panel-default page">',
            '   <div class="panel-heading">',
            '       <ul class="breadcrumb">',
            '       </ul>',
            '   </div>',
            '   <div class="panel-body page-content">',
            '   </div>',
            '</div>'
        ].join(''));
        this.breadcrumbContainer = this.frame.find('.breadcrumb');
        this.contentContainer = this.frame.find('.page-content');
        //滚动初始化
        this.showStack.add('scroll', function(){
            $(document).scrollTop(self.scrollTop);
        });
        this.hideStack.add('scroll', function(){
            self.scrollTop = $(document).scrollTop();
        });
        //初始化栈
        this.initStack.fire();
        //添加到DOM
        App.mainContainer.append(this.frame);
        this.inited = true;
    },
    //设置面包屑导航
    setBreadcrumb: function(items){
        this.breadcrumb = items;
        //清空内容
        this.breadcrumbContainer.children().remove();
        for(var i in items){
            var item = items[i];
            var li = $('<li>'+item.content+'</li>');
            if(item.icon){
                li.prepend($('<span style="display:inline-block;width: 5px;"></span>'));
                li.prepend(typeof item.content === 'string' ? $(item.icon) : item.icon);
            }
            if(item.active){
                li.addClass('active');
            }
            if(item.action){
                li.click((function(){
                    var item_ = item;
                    return function(){
                        item_.action();
                    };
                })());
            }
            this.breadcrumbContainer.append(li);
        }
        return this;
    },
    //设置内容
    setContent: function(content){
        //清空内容
        this.contentContainer.children().each(function(){
            $(this).remove();
        });
        //设置内容
        if(typeof content === 'string'){
            this.contentContainer.html(content);
        }else{
            this.contentContainer.append(content);
        }
        return this;
    },
    //设置参数
    setParams: function(params){
        this.params = params;
        return this;
    },
    //显示
    show: function(){
        //切换标题
        App.titleContainer.html(this.breadcrumbContainer.html());
        this.frame.show();
        this.showStack.fire();
        if(this.isDirty){
            this.refresh();
        }
        return this;
    },
    //隐藏
    hide: function(){
        this.hideStack.fire();
        this.frame.hide();
        return this;
    },
    //刷新
    refresh: function(){
        this.refreshStack.fire();
        this.isDirty = false;
        return this;
    },
    //污染
    dirty: function(){
        if(this.inited){
            this.isDirty = true;
        }
        return this;
    },
    //销毁
    destroy: function(){
        //弹窗销毁
        for(var i in this.wins){
            this.wins[i].destroy();
        }
        //页面销毁
        this.frame.remove();
        this.frame = null;
        //销毁栈
        this.destroyStack.fire();
        this.inited = false;
        return this;
    }
};
//模型
App.Model = function(conf){
    //私有属性
    this.name = conf.name;
    this.url = conf.url;
    this.data = null;
    this.isFresh = false;
    this.isLoading = false;
    this.errorTimes = 0;//网络错误次数
    this.task = new App.Model.Task();
};
App.Model.prototype = {
    constructor: App.Model,
    addTask: function(callback, name, flush){
        var taskName = this.task.push(callback, name, flush);
        if(this.isFresh){
            callback(this.get());
        }else if(!this.isLoading){
            this.refresh();
        }
        return taskName;
    },
    removeTask: function(name){
        this.task.drop(name);
    },
    refresh: function(){
        this.loadData();
    },
    loadData: function(){
        var self = this;
        self.isLoading = true;
        App.util.request.getData({
            url: self.url,
            loading: false,
            success: function(data){
                self.isLoading = false;
                self.isFresh = true;
                self.task.apply(data.data);
                self.set(data.data);
                self.errorTimes = 0;
            },
            error: function(){
                if(self.errorTimes < 8){
                    self.errorTimes++;
                }
                setTimeout(function(){
                    self.loadData();
                }, 1000*self.errorTimes*self.errorTimes);
            }
        });
    },
    get: function(){
        return this.data;
    },
    set: function(data){
        this.data = data;
    }
};
//任务栈
App.Model.Task = function (){
    this.stack = [];
    this.autoKey = 0;
};
App.Model.Task.prototype = {
    push: function(callback, name, flash){
        var self = this;
        var item = {
            name: name || '_'+self.autoKey,
            callback: callback,
            flash: flash ? true : false
        };
        this.stack.push(item);
        this.autoKey++;
        return item.name;
    },
    pop: function(){
        return this.stack.pop();
    },
    drop: function(name){
        var index = this.index(name);
        if(index > -1){
            this.stack.splice(index, 1);
        }
    },
    apply: function(data){
        for(var i in this.stack){
            var item = this.stack[i];
            item.callback(data);
            if(item.flash){
                this.drop(item.name);
            }
        }
    },
    all: function(){
        return this.stack;
    },
    find: function(name){
        for(var i in this.stack){
            if(this.stack[i].name === name){
                return this.stack[i];
            }
        }
        return false;
    },
    index: function(name){//索引值
        for(var i in this.stack){
            if(this.stack[i].name === name){
                return parseInt(i);
            }
        }
        return -1;
    },
    last: function(){
        return this.stack[this.stack.length - 1];
    },
    toString: function(){
        var res = [];
        for(var i in this.stack){
            res.push(this.stack[i].name);
        }
        return JSON.stringify(res);
    }
};
App.Form = function(config){
    //参数配置初始化
    this.config = config;
    //私有属性
    this.status = 'add';
    this.formItems = {};
    this.items = {};
    this.originalData = null;
    this.frame = null;
    this.loading = null;
    //初始化组件
    this.init();
};
App.Form.prototype = {
    construtor: App.Form,
    init: function(){
        //初始化
        this.frame = $([
            '<div class="form-container">',
            '   <table class="table-form">',
            '       <thead>',
            '           <tr>',
            '               <th> </th>',
            '               <td> </th>',
            '               <th> </th>',
            '               <td> </th>',
            '           </tr>',
            '       </thead>',
            '       <tbody></tbody>',
            '   </table>',
            '</div>'
        ].join(''));
        var items = this.config.items;
        var small = App.size === 'sm';
        var hard = this.config.hard || false;
        for(var i in items){
            if(!hard && small && items[i].length > 1){
                for(var j in items[i]){
                    this.getLine(items[i][j]).appendTo(this.frame.find('> table > tbody'));
                }
            }else{
                this.getLine(items[i]).appendTo(this.frame.find('> table > tbody'));
            }
        }
    },
    fix: function(container){
        this.frame.appendTo(container);
        this.loading = new App.Loading({container: container});
    },
    getLine: function(items){
        if(!items.length){
            items = [items];
        }
        var tr = $('<tr></tr>');
        for(var i in items){
            var config = items[i];
            if(config.type === 'hidden'){
                tr.hide();
            }
            var item = this.getItem(config, items.length > 1);
            this.items[config.name] = item;
            item.th.appendTo(tr);
            item.td.appendTo(tr);
        }
        return tr;
    },
    getItem: function(item, half){
        var self = this;
        return {
            th: null,
            td: null,
            form: null,
            init: function(){
                var title = item.title || '';
                this.th = $('<th class="name"><span class="form-item-name '+(item.required ? 'required' : '')+'">'+title+'</span></th>');
                this.td = $('<td colspan="3"></td>');
                var formItem = self.getFormItem(item);
                this.form = formItem;
                self.formItems[formItem.name] = formItem;
                this.td.append(formItem.frame);
                if(half){
                    this.td.removeAttr('colspan');
                }
                return this;
            },
            show: function(){
                this.th.show();
                this.td.show();
            },
            hide: function(){
                this.th.hide();
                this.td.hide();
            }
        }.init();
    },
    getFormItem: function(config){
        var formItem = null;
        var itemClass = App.Form.lib[config.type];
        config.form = this;
        if(itemClass){
            formItem = new itemClass(config);
        }else{
            alert('Form item "'+config.type+'" not fond!');
        }
        if(config.value && formItem){
            formItem.change(config.value);
        }
        formItem.setForm(this);
        return formItem;
    },
    change: function(data){
        this.originalData = data;
        if(this.config.beforeChange){
            this.config.beforeChange(this, data);
        }
        for(var i in data){
            if(this.formItems[i]){
                this.formItems[i].change(data[i]);
            }
        }
        if(this.config.onChange){
            this.config.onChange(this, data);
        }
        return this;
    },
    reset: function(){
        for(var i in this.items){
            this.items[i].form.reset();
        }
        if(this.config.onReset){
            this.config.onReset(this);
        }
        return this;
    },
    add: function(){
        this.status = 'add';
        var fields = this.getFields();
        for(var i in this.items){
            this.items[i].form.edit();
            if(fields.indexOf(i) > -1){
                this.items[i].form.enable();
                this.items[i].show();
            }else{
                this.items[i].form.disable();
                this.items[i].hide();
            }
        }
        if(this.config.onAdd){
            this.config.onAdd(this);
        }
        return this;
    },
    edit: function(){
        this.status = 'edit';
        var fields = this.getFields();
        for(var i in this.items){
            this.items[i].form.edit();
            if(fields.indexOf(i) > -1){
                this.items[i].form.enable();
                this.items[i].show();
            }else{
                this.items[i].form.disable();
                this.items[i].hide();
            }
        }
        if(this.config.onEdit){
            this.config.onEdit(this);
        }
        return this;
    },
    view: function(){
        this.status = 'view';
        var fields = this.getFields();
        for(var i in this.items){
            if(fields.indexOf(i) > -1){
                this.items[i].show();
            }else{
                this.items[i].hide();
            }
        }
        if(this.config.onView){
            this.config.onView(this);
        }
        return this;
    },
    flushView: function(){
        for(var i in this.items){
            this.items[i].form.view();
        }
        return this;
    },
    getData: function(){
        if(this.config.onSubmit){
            this.config.onSubmit(this);
        }
        var data = {};
        var fields = this.getFields();
        for(var i in this.formItems){
            if(fields.indexOf(i) < 0){
                continue;
            }
            var item = this.formItems[i];
            data[item.name] = item.value();
        }
        return data;
    },
    fillFromUrl: function(url, after){
        var self = this;
        App.util.request.getData({
            url: url,
            loading: self.loading,
            success: function(data){
                self.change(data);
                if(after){
                    after(self);
                }
            }
        });
    },
    getFields: function(){
        var exp = 'all';
        if(this.status === 'add'){
            exp = this.config.add || 'all';
        }else if(this.status === 'edit'){
            exp = this.config.edit || 'all';
        }else if(this.status === 'view'){
            exp = this.config.view || 'all';
        }
        var fields = [];
        if(exp.toLowerCase() === 'all'){//所有
            for(var i in this.formItems){
                fields.push(i);
            }
        }else if(exp.indexOf('except:') === 0){//排除
            var excepts = exp.replace('except:', '').split(',');
            for(var i in this.formItems){
                if(excepts.indexOf(i) > -1){
                    continue;
                }
                fields.push(i);
            }
        }else{//指定
            fields = exp.split(',');
        }
        return fields;
    },
    validate: function(){
        var rs = true;
        var fields = this.getFields();
        for(var i in this.formItems){
            if(fields.indexOf(i) < 0){
                continue;
            }
            if(!this.formItems[i].validate()){
                rs = false;
            }
        }
        if(!rs){
            App.util.hint.toast({
                type: 'warning',
                text: '数据格式有误'
            }).delay(1000);
        }
        return rs;
    },
    destroy: function(){
        for(var i in this.formItems){
            // if(!this.formItems[i].destroy){
            //     alert(this.formItems[i].name);
            // }
            this.formItems[i].destroy();
        }
        this.frame.remove();
        this.items = [];
        this.formItems = [];
    }
};
App.Form.lib = {};
//表单项-输入框
App.Form.Input = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputGroup = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Input.prototype = {
    construtor: App.Form.Input,
    init: function(){
        var self = this;
        var name = this.config.name || '';
        var title = this.config.title ? '点击输入'+this.config.title : '';
        this.frame = $(`
            <div class="input-container">
                <div class="input-group" style="width:100%">
                    <input name="${name}" class="form-control form-input" type="text" placeholder="${title}"/>
                </div>
                <span class="form-static" style="display: none;"></span>
            </div>
        `);
        this.inputGroup = this.frame.find('> .input-group').first();
        this.inputEle = this.inputGroup.find('> input').first();
        this.viewEle = this.frame.find('.form-static').first(); 
        this.inputEle.change(() => {
            this.onChange();
            this.viewEle.text(this.inputEle.val());
        });
        if(this.config.unit){
            this.frame.find('.input-group').append($('<span class="input-group-addon">'+this.config.unit+'</span>'));
        }
        if(this.config.comment){
            this.frame.append($('<div class="input-comment">'+this.config.comment+'</div>'));
        }
        if(this.config.maxLength){
            var allowLength = parseInt(this.config.maxLength);
            var maxLengthFrame = $([
                '<div class="input-max-length">',
                '    <span class="num-current">0</span> / <span class="num-all"></span>',
                '</div>'
            ].join(''));
            maxLengthFrame.find('.num-all').text(allowLength);
            var nameChange = function(){
                var length = App.util.str.len(self.inputEle.val());
                maxLengthFrame.find('.num-current').text(length);
                if(length > allowLength){
                    self.inputEle.addClass('error');
                }else{
                    self.inputEle.removeClass('error');
                }
            };
            this.inputEle.css({'padding-right': '55px'}).keyup(function(){
                nameChange();
            }).change(function(){
                nameChange();
            });
            maxLengthFrame.appendTo(this.frame.find('.input-group'));
        }
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        this.inputGroup.hide();
        this.viewEle.text(this.inputEle.val()).show();
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputGroup.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['input'] = App.Form.Input;
//表单项-金额
App.Form.Amount = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputGroup = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Amount.prototype = {
    construtor: App.Form.Amount,
    init: function(){
        var self = this;
        var name = this.config.name || '';
        var title = this.config.title ? '点击输入'+this.config.title : '';
        this.frame = $([
            '<div class="input-container">',
            '   <div class="input-group">',
            '       <input name="'+name+'" class="form-control" type="text" placeholder="'+title+'"/>',
            '       <span class="input-group-addon">¥</span>',
            '   </div>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(' '));
        this.inputGroup = this.frame.find('> .input-group').first();
        this.inputEle = this.inputGroup.find('> input').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        this.inputGroup.hide();
        this.viewEle.text(this.inputEle.val()).show();
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputGroup.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['amount'] = App.Form.Amount;
//表单项-金额
App.Form.Percent = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputGroup = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Percent.prototype = {
    construtor: App.Form.Percent,
    init: function(){
        var self = this;
        var name = this.config.name || '';
        var title = this.config.title ? '点击输入'+this.config.title : '';
        this.frame = $([
            '<div class="input-container">',
            '   <div class="input-group">',
            '       <input name="'+name+'" class="form-control" type="text" placeholder="'+title+'"/>',
            '       <span class="input-group-addon">%</span>',
            '   </div>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(' '));
        this.inputGroup = this.frame.find('> .input-group').first();
        this.inputEle = this.inputGroup.find('> input').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        this.inputGroup.hide();
        this.viewEle.text(this.inputEle.val()).show();
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputGroup.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['percent'] = App.Form.Percent;
//表单项-隐藏域
App.Form.Hidden = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    //初始化
    this.init = function(){
        var self = this;
        this.frame = $('<input name="'+self.name+'" type="hidden"/>');
        this.name = this.config.name;
        this.inputEle = this.frame;
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    };
    this.init();
};
App.Form.Hidden.prototype = {
    construtor: App.Form.Hidden,
    init: function(){
        var self = this;
        this.frame = $('<input name="'+self.name+'" type="hidden"/>');
        this.inputEle = this.frame;
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        if(this.config.defaults){
            this.change(this.config.defaults);
        }
        return this;
    },
    edit: function(){
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['hidden'] = App.Form.Hidden;
//表单项-密码框
App.Form.Password = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Password.prototype = {
    construtor: App.Form.Password,
    init: function(){
        var self = this;
        var name = this.config.name || '';
        var title = this.config.title ? '点击输入'+this.config.title : '';
        this.frame = $([
            '<div class="input-container">',
            '   <input name="'+name+'" type="password" class="form-control" placeholder="'+title+'"/>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(' '));
        this.inputEle = this.frame.find('> input').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        this.inputEle.hide();
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputEle.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['password'] = App.Form.Password;
//表单项-多选框
App.Form.Checkbox = function(config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.viewEle = null;
    this.modelTaskName = null;
    this.model = null;
    this.valTemp = null;
    //初始化
    this.init();
};
App.Form.Checkbox.prototype = {
    constructor: App.Form.Checkbox,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="checkbox-container">',
            '   <div class="checkboxes"></div>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(''));
        this.inputEle = this.frame.find('> .checkboxes').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        this.flush(true);
    },
    flush: function(isInit){
        var self = this;
        var items = this.config.items || [];
        if(items instanceof App.Model){
            self.model = items;
            if(!isInit){
                items.removeTask(self.modelTaskName);
            }
            self.modelTaskName = items.addTask(function(items){
                self.render(items, isInit);
            });
        }else{
            self.render(items, isInit);
        }
    },
    setForm: function(form){
        this.form = form;
    },
    render: function(items, isInit){
        var oldValue = this.valTemp || this.value();
        var container = this.frame.find('.checkboxes');
        container.html('');
        var name = this.config.name || '';
        var item_key = this.config.item_key || 0;
        var item_name = this.config.item_name || 1;
        if(this.config.filter){
            items = this.config.filter(App.util.copy(items, true));
        }
        var key = '';
        var title = '';
        for(var i in items){
            key = typeof item_key === 'function' ? item_key(items[i]) : items[i][item_key];
            title = typeof item_name === 'function' ? item_name(items[i]) : items[i][item_name];
            $([
                '<label class="form-checkbox">',
                '    <input type="checkbox" name="'+name+'[]" value="'+key+'">',
                '    <span>'+title+'</span>',
                '</label>'
            ].join('')).appendTo(container);
        }
        if(!isInit){
            this.change(oldValue);
        }
    },
    change: function(vals){//改变
        var self = this;
        this.valTemp = vals;
        self.inputEle.find('input').prop('checked', false);
        var changeValue = function(){
            vals = vals ? (typeof vals === 'array' || typeof vals === 'object' ? vals : vals.split(',')) : [];
            var key = self.config.value_key;
            for(var i in vals){
                var val = key ? vals[i][key] : vals[i];
                self.inputEle.find('input[value="'+val+'"]').prop('checked', true).change();
            }
        };
        if(self.items instanceof App.Model){
            self.items.addTask(function(){
                changeValue();
            }, null, true);
        }else{
            changeValue();
        }
        return this;
    },
    view: function(){//查看
        this.inputEle.hide();
        this.viewEle.text(this.labels().join('、')).show();
        return this;
    },
    reset: function(){
        this.valTemp = [];
        this.inputEle.find('input').prop('checked', false).change();
        if(this.config.defaults){
            this.change(this.config.defaults);
        }
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputEle.show();
        return this;
    },
    value: function(){
        var vals = [];
        this.inputEle.find('input').each(function(){
            if($(this).prop('checked')){
                vals.push($(this).attr('value'));
            }
        });
        return vals.join(',');
    },
    labels: function(){
        var labels = [];
        this.inputEle.find('input').each(function(){
            if($(this).prop('checked')){
                labels.push($(this).next().text());
            }
        });
        return labels;
    },
    enable: function(){
        this.inputEle.find('input').prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.find('input').prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        if(this.model){
            this.model.removeTask(this.modelTaskName);
        }
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['checkbox'] = App.Form.Checkbox;
//表单项-块式多选
App.Form.CheckboxBlock = function (config){
    this.checkbox = null;
    this.init(config);
    return this.checkbox;
};
App.Form.CheckboxBlock.prototype = {
    init: function(config){
        var checkbox = new App.Form.Checkbox(config);
        checkbox.render = function(items, isInit){
            var oldValue = this.valTemp || this.value();
            var container = this.frame.find('.checkboxes');
            container.html('');
            var name = this.config.name || '';
            var item_key = this.config.item_key || 0;
            var item_name = this.config.item_name || 1;
            if(checkbox.config.filter){
                items = this.config.filter(App.util.copy(items, true));
            }
            var key = '';
            var title = '';
            for(var i in items){
                key = typeof item_key === 'function' ? item_key(items[i]) : items[i][item_key];
                title = typeof item_name === 'function' ? item_name(items[i]) : items[i][item_name];
                $([
                    '<label class="form-checkbox">',
                    '    <input type="checkbox" name="'+name+'[]" value="'+key+'">',
                    '    <span>'+title+'</span>',
                    '    <i class="fa fa-check-square fa-sm checked-icon"></i>',
                    '</label>'
                ].join('')).appendTo(container);
            }
            if(!isInit){
                checkbox.change(oldValue);
            }
        };
        checkbox.onChange = function(){
            checkbox.frame.find('input[type="checkbox"]').each(function(){
                if($(this).prop('checked')){
                    $(this).parent().addClass('checked');
                }else{
                    $(this).parent().removeClass('checked');
                }
            });
            if(checkbox.config.onChange){
                checkbox.config.onChange(this);
            }
        };
        checkbox.frame.find('.checkboxes').addClass('checkboxes-block');
        checkbox.flush();
        this.checkbox = checkbox;
    }
};
//添加到库
App.Form.lib['checkbox_block'] = App.Form.CheckboxBlock;
//表单项-单选框
App.Form.Radio = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.viewEle = null;
    this.modelTaskName = null;
    this.model = null;
    this.valTemp = null;
    //初始化
    this.init();
};
App.Form.Radio.prototype = {
    construtor: App.Form.Radio,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="radio-container">',
            '   <div class="radios"></div>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(''));
        this.inputEle = this.frame.find('> .radios').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        this.flush(true);
    },
    flush: function(isInit){
        var self = this;
        var items = this.config.items || [];
        if(items instanceof App.Model){
            self.model = items;
            if(!isInit){
                items.removeTask(self.modelTaskName);
            }
            self.modelTaskName = items.addTask(function(items){
                self.render(items, isInit);
            });
        }else{
            self.render(items, isInit);
        }
    },
    setForm: function(form){
        this.form = form;
    },
    render: function(items, isInit){
        var oldValue = this.valTemp || this.value();
        var container = this.frame.find('.radios');
        container.html('');
        var name = this.config.name || '';
        var item_key = this.config.item_key || 0;
        var item_name = this.config.item_name || 1;
        if(this.config.filter){
            items = this.config.filter(App.util.copy(items, true));
        }
        var key = '';
        var title = '';
        for(var i in items){
            key = typeof item_key === 'function' ? item_key(items[i]) : items[i][item_key];
            title = typeof item_name === 'function' ? item_name(items[i]) : items[i][item_name];
            $([
                '<label class="form-radio">',
                '    <input type="radio" name="'+name+'[]" value="'+key+'">',
                '    <span>'+title+'</span>',
                '</label>'
            ].join('')).appendTo(container);
        }
        if(!isInit){
            this.change(oldValue);
        }
    },
    change: function(val){//改变
        var self = this;
        this.valTemp = val;
        if(self.items instanceof App.Model){
            self.items.addTask(function(){
                self.inputEle.find('input').prop('checked', false);
                self.inputEle.find('input[value="'+val+'"]').prop('checked', true).change();
            }, null, true);
        }else{
            self.inputEle.find('input').prop('checked', false);
            self.inputEle.find('input[value="'+val+'"]').prop('checked', true).change();
        }
        return this;
    },
    view: function(){//查看
        this.inputEle.hide();
        this.viewEle.text(this.label()).show();
        return this;
    },
    reset: function(){
        this.valTemp = [];
        this.viewEle.hide();
        this.inputEle.find('input').prop('checked', false).change();
        if(this.config.defaults){
            this.change(this.config.defaults);
        }
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputEle.show();
        return this;
    },
    value: function(){
        var val = '';
        this.inputEle.find('input').each(function(){
            if($(this).is(":checked")){
                val = $(this).val();
            }
        });
        return val;
    },
    label: function(){
        var label = '';
        this.inputEle.find('input').each(function(){
            if($(this).prop('checked')){
                label = $(this).next().text();
            }
        });
        return label;
    },
    enable: function(){
        this.inputEle.find('input').prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.find('input').prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        if(this.model){
            this.model.removeTask(this.modelTaskName);
        }
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['radio'] = App.Form.Radio;
//表单项-块式单选
App.Form.RadioBlock = function (config){
    this.radio = null;
    this.init(config);
    return this.radio;
};
App.Form.RadioBlock.prototype = {
    init: function(config){
        var radio = new App.Form.Radio(config);
        radio.render = function(items, isInit){
            var oldValue = this.valTemp || this.value();
            var container = this.frame.find('.radios');
            container.html('');
            var name = this.config.name || '';
            var item_key = this.config.item_key || 0;
            var item_name = this.config.item_name || 1;
            if(radio.config.filter){
                items = this.config.filter(App.util.copy(items, true));
            }
            var key = '';
            var title = '';
            for(var i in items){
                key = typeof item_key === 'function' ? item_key(items[i]) : items[i][item_key];
                title = typeof item_name === 'function' ? item_name(items[i]) : items[i][item_name];
                $([
                    '<label class="form-radio">',
                    '    <input type="radio" name="'+name+'[]" value="'+key+'">',
                    '    <span>'+title+'</span>',
                    '    <i class="fa fa-check-circle fa-sm checked-icon"></i>',
                    '</label>'
                ].join('')).appendTo(container);
            }
            if(!isInit){
                radio.change(oldValue);
            }
        };
        radio.onChange = function(){
            radio.frame.find('input[type="radio"]').each(function(){
                if($(this).prop('checked')){
                    $(this).parent().addClass('checked');
                }else{
                    $(this).parent().removeClass('checked');
                }
            });
            if(radio.config.onChange){
                radio.config.onChange(this);
            }
        };
        radio.frame.find('.radios').addClass('radios-block');
        radio.flush();
        this.radio = radio;
    }
};
//添加到库
App.Form.lib['radio_block'] = App.Form.RadioBlock;
//表单项-下拉框
App.Form.Select = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.inputEle = null;
    this.viewEle = null;
    this.modelTaskName = null;
    this.model = null;
    this.valTemp = null;
    //初始化
    this.init();
};
App.Form.Select.prototype = {
    construtor: App.Form.Select,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="input-container">',
            '   <select name="'+self.name+'" class="form-control">',
            '   </select>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(''));
        this.inputEle = this.frame.find('select').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        this.flush(true);
    },
    flush: function(isInit){
        var self = this;
        var items = this.config.items || [];
        if(items instanceof App.Model){
            self.model = items;
            if(!isInit){
                items.removeTask(self.modelTaskName);
            }
            self.modelTaskName = items.addTask(function(items){
                self.render(items, isInit);
            });
        }else{
            self.render(items, isInit);
        }
    },
    setForm: function(form){
        this.form = form;
    },
    render: function(items, isInit){
        var oldValue = this.valTemp || this.value();
        this.changeOptions(items);
        if(!isInit){
            var $select = this.frame.find('select').first();
            if($select.find('option[value="'+oldValue+'"]').length > 0){
                this.change(oldValue);
            }else{
                this.reset();
            }
        }
    },
    changeOptions(items){
        var item_key = this.config.item_key || 0;
        var item_name = this.config.item_name || 1;
        if(this.config.filter){
            items = this.config.filter(App.util.copy(items, true));
        }
        var title = this.config.title ? '选择'+this.config.title : '';
        var container = this.frame.find('select');
        container.html('<option value="">'+title+'</option>');
        var key = '';
        var title = '';
        for(var i in items){
            key = typeof item_key === 'function' ? item_key(items[i]) : items[i][item_key];
            title = typeof item_name === 'function' ? item_name(items[i]) : items[i][item_name];
            $('<option value="'+key+'">'+title+'</option>').appendTo(container);
        }
    },
    change: function(val){//改变
        var self = this;
        this.valTemp = val;
        if(self.config.items instanceof App.Model){
            self.config.items.addTask(function(){
                self.inputEle.val(val).change();
            }, null, true);
        }else{
            self.inputEle.val(val).change();
        }
        return this;
    },
    view: function(){//查看
        this.inputEle.hide();
        this.viewEle.text(this.text()).show();
        return this;
    },
    reset: function(){
        this.valTemp = '';
        this.inputEle.val('').change();
        this.viewEle.text('');
        if(this.config.defaults){
            this.change(this.config.defaults);
        }
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputEle.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    text: function(){
        var value = this.value();
        var title = '';
        if(value !== ''){
            this.inputEle.find('option').each(function(){
                if($(this).attr('value')+'' === value+''){
                    title = $(this).text();
                }
            });
        }
        return title;
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        if(this.model){
            this.model.removeTask(this.modelTaskName);
        }
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['select'] = App.Form.Select;
//表单项-文本域
App.Form.Textarea = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Textarea.prototype = {
    constructor: App.Form.Textarea,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="input-container">',
            '   <textarea name="'+self.name+'" type="text" class="form-control" placeholder="'+(self.config.title ? '点击输入'+self.config.title : '')+'"></textarea>',
            '   <p class="form-static" style="display: none;"></p>',
            '</div>'
        ].join(' '));
        this.inputEle = this.frame.find('> textarea').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        if(this.config.comment){
            this.frame.append($('<div class="input-comment">'+this.config.comment+'</div>'));
        }
        if(this.config.maxLength){
            var allowLength = parseInt(this.config.maxLength);
            var maxLengthFrame = $([
                '<div class="input-max-length">',
                '    <span class="num-current">0</span> / <span class="num-all"></span>',
                '</div>'
            ].join(''));
            maxLengthFrame.find('.num-all').text(allowLength);
            var nameChange = function(){
                var length = App.util.str.len(self.inputEle.val());
                maxLengthFrame.find('.num-current').text(length);
                if(length > allowLength){
                    self.inputEle.addClass('error');
                }else{
                    self.inputEle.removeClass('error');
                }
            };
            this.inputEle.keyup(function(){
                nameChange();
            }).change(function(){
                nameChange();
            });
            maxLengthFrame.appendTo(this.frame);
        }
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.val(val).change();
        return this;
    },
    view: function(){//查看
        this.inputEle.hide();
        var val = this.inputEle.val().replace(/\r/g,"").replace(/\n/g, "<br/>");
        this.viewEle.html(val).show();
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        this.viewEle.hide();
        this.inputEle.show();
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.inputEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.inputEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.inputEle.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['textarea'] = App.Form.Textarea;
//表单项-静态控件
App.Form.Static = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.viewEle = null;
    //初始化
    this.init();
};
App.Form.Static.prototype = {
    construtor: App.Form.Static,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="input-container">',
            '   <input name="'+self.name+'" type="hidden"/>',
            '   <span class="form-static"></span>',
            '</div>'
        ].join(' '));
        this.inputEle = this.frame.find('> input').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        var value = val ? (typeof val === 'string' || typeof val === 'number' ? val : val[0]) : '';
        var text = val ? (typeof val === 'string' || typeof val === 'number' ? val : val[1]) : '';
        this.inputEle.val(value).change();
        this.viewEle.text(text);
        return this;
    },
    view: function(){//查看
        return this;
    },
    reset: function(){
        this.inputEle.val('').change();
        this.viewEle.text('');
        return this;
    },
    edit: function(){
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            self.frame.addClass('error');
            self.inputEle.one('change', function(){
                self.frame.removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['static'] = App.Form.Static;
//表单项-附件上传
App.Form.Attachment = function(config){
    this.config = config;
    this.form = null;
    this.name = null;
    this.frame = null;
    //初始化
    this.init();
};
App.Form.Attachment.prototype = {
    constructor: App.Form.Attachment,
    init: function(){
        var self = this;
        this.name = self.config.name;
        this.frame = $([
            '<div class="attachment-upload-container">',
            '   <div class="item main">',
            '       <div class="progress">',
            '           <div class="progress-bar progress-bar-success" style="width: 12%;"></div>',
            '           <div class="percent">12%</div>',
            '       </div>',
            '       <div class="input-group">',
            '           <input type="text" class="form-control attachment-upload-name" placeholder="未上传文件"/>',
            '           <span class="input-group-btn clear-btn-container">',
            '               <button class="btn btn-default btn-sm attachment-upload-clear-btn" type="button" style="border-radius: 0;border-left:none;"><i class="fa fa-trash-o"></i></button>',
            '           </span>',
            '           <span class="input-group-btn download-btn-container">',
            '               <a class="btn btn-default btn-sm attachment-upload-download-btn" style="border-radius: 0;border-left:none;" href="javascript:void(0);" download="w3logo" target="_blank"><i class="fa fa-download"></i></a>',
            '           </span>',
            '           <span class="input-group-btn upload-btn-container">',
            '               <button class="btn btn-default btn-sm attachment-upload-upload-btn" type="button" style="border-radius: 0;"><i class="fa fa-upload"></i></button>',
            '           </span>',
            '       </div>',
            '       <input class="attachment-upload-file" type="file" style="display: none;"/>',
            '       <input class="attachment-upload-value" type="hidden"/>',
            '   </div>',
            '</div>'
        ].join(' '));
        this.frame.find('.attachment-upload-value').attr('name', this.name);
        //上传功能初始化
        this.frame.find('.attachment-upload-upload-btn').click(function(){
            self.frame.find('.attachment-upload-file').click();
        });
        //触发上传
        this.frame.find('.attachment-upload-file').change(function(){
            if(this.files.length > 0){
                self.upload(this.files[0]);
                this.value = '';
            }
        });
        //删除
        this.frame.find('.attachment-upload-clear-btn').click(function(){
            self.change('');
        });
        //修改文件名
        this.frame.find('.attachment-upload-name').change(function(){
            var valRaw = self.value();
            if(valRaw){
                var val = JSON.parse(valRaw);
                val.name = $(this).val();
                self.frame.find('.attachment-upload-value').val(JSON.stringify(val)).change();
            }
        });
        this.frame.find('.attachment-upload-value').change(function(){
            var valRaw = self.value();
            if(valRaw){
                var val = JSON.parse(valRaw);
                self.frame.find('.attachment-upload-download-btn').attr('href', App.util.formator.fileUrl(val.url)+'?attname='+val.name);
                self.frame.find('.attachment-upload-download-btn').attr('download', val.name.replace(/\..*$/, ''));
            }else{
                self.frame.find('.attachment-upload-download-btn').attr('href', 'javascript:void(0);');
                self.frame.find('.attachment-upload-download-btn').removeAttr('download');
            }
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    upload: function(file){
        var self = this;
        //显示进度
        this.frame.find('.progress').show();
        this.frame.find('.progress .percent').text('0%');
        this.frame.find('.progress .progress-bar').css({width: '0%'});
        App.util.uploader.upload({
            file: file,
            key: function(key){
                return key+(/\..*$/.exec(file.name));
            },
            progress: function(percent){
                self.frame.find('.progress .percent').text(percent+'%');
                self.frame.find('.progress .progress-bar').css({width: percent+'%'});
            },
            success: function(key){
                var val = {
                    url: key,
                    size: file.size,
                    name: file.name,
                    type: file.type
                };
                self.frame.find('.progress').hide();
                self.change(val);
                if(self.config.afterUpload){
                    self.config.afterUpload(key);
                }
            }
        }).start();
    },
    change: function(val){//改变
        if(typeof val === 'string' && val.length > 0){
            val = JSON.parse(val);
        }
        if(!val){
            this.frame.find('.attachment-upload-value').val('').change();
            this.frame.find('.attachment-upload-name').val('');
            this.frame.find('.clear-btn-container').hide();
            this.frame.find('.download-btn-container').hide();
            this.frame.find('.upload-btn-container').show();
            return this;
        }
        this.frame.find('.attachment-upload-value').val(JSON.stringify(val)).change();
        this.frame.find('.attachment-upload-name').val(val.name);
        this.frame.find('.clear-btn-container').show();
        this.frame.find('.download-btn-container').hide();
        return this;
    },
    view: function(){//查看
        this.frame.find('.attachment-upload-name').prop('disabled', true);
        this.frame.find('.clear-btn-container').hide();
        this.frame.find('.download-btn-container').show();
        this.frame.find('.upload-btn-container').hide();
        return this;
    },
    reset: function(){
        this.change('');
        return this;
    },
    edit: function(){
        this.frame.find('.attachment-upload-name').prop('disabled', false);
        this.frame.find('.download-btn-container').hide();
        this.frame.find('.upload-btn-container').show();
        return this;
    },
    value: function(){
        return this.frame.find('.attachment-upload-value').val();
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error');
            this.frame.find('.attachment-upload-value').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['attachment'] = App.Form.Attachment;
//表单项-附件上传
App.Form.Attachments = function(config){
    this.config = config;
    this.form = null;
    this.name = null;
    this.frame = null;
    this.items = [];
    this.mainItem = null;
    this.counter = 0;
    //初始化
    this.init();
};
App.Form.Attachments.prototype = {
    constructor: App.Form.Attachments,
    init: function(){
        var self = this;
        this.name = self.config.name;
        this.frame = $([
            '<div class="attachments-upload-container">',
            '</div>'
        ].join(' '));
        this.mainItem = new App.Form.Attachment({name: self.config.name+'_'+self.counter});
        this.mainItem.frame.find('.input-group .attachment-upload-upload-btn').css({'border-left': 'none'});
        this.mainItem.frame.find('.input-group').append($([
            '<span class="input-group-btn" style="">',
            '    <a class="btn btn-default btn-sm attachment-add-btn" style="border-radius: 0;" href="javascript:void(0);"><i class="fa fa-plus"></i></a>',
            '</span>'
        ].join('')));
        this.frame.append(this.mainItem.frame);
        this.mainItem.frame.find('.input-group .attachment-add-btn').click(function(){
            self.addItem();
        });
        this.items.push(this.mainItem);
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        if(typeof val === 'string' && val.length > 0){
            val = JSON.parse(val);
        }else{
            val = [];
        }
        this.reset();
        for(var i in val){
            if(i < 1){
                this.mainItem.change(val[i]);
                continue;
            }
            var item = this.addItem();
            item.change(val[i]);
        }
        return this;
    },
    addItem: function(){
        var self = this;
        this.counter++;
        var name = self.config.name+'_'+self.counter;
        var item = new App.Form.Attachment({name: name});
        this.frame.append(item.frame);
        item.frame.css({'margin-top': '5px'});
        item.frame.find('.input-group .attachment-upload-upload-btn').css({'border-left': 'none'});
        item.frame.find('.input-group').append($([
            '<span class="input-group-btn" style="">',
            '    <a class="btn btn-default btn-sm attachment-delete-btn" style="border-radius: 0;" href="javascript:void(0);"><i class="fa fa-times"></i></a>',
            '</span>'
        ].join('')));
        item.frame.find('.input-group .attachment-delete-btn').click(function(){
            self.removeItem(name);
        });
        this.items.push(item);
        return item;
    },
    removeItem: function(name){
        for(var i in this.items){
            if(this.items[i].name === name){
                this.items[i].destroy();
                this.items.splice(i, 1);
                break;
            }
        }
    },
    view: function(){//查看
        for(var i in this.items){
            this.items[i].view();
            this.items[i].frame.find('.attachment-add-btn').hide();
            this.items[i].frame.find('.attachment-delete-btn').hide();
        }
        return this;
    },
    reset: function(){
        this.mainItem.reset();
        for(var i in this.items){
            if(i < 1){
                continue;
            }
            this.items[i].destroy();
        }
        this.items = [this.mainItem];
        return this;
    },
    edit: function(){
        for(var i in this.items){
            this.items[i].edit();
            this.items[i].frame.find('.attachment-add-btn').hide();
            this.items[i].frame.find('.attachment-delete-btn').hide();
        }
        return this;
    },
    value: function(){
        var vals = [];
        for(var i in this.items){
            vals.push(JSON.parse(this.items[i].value()));
        }
        return JSON.stringify(vals);
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        return true;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        for(var i in this.items){
            this.items[i].destroy();
        }
    }
};
//添加到库
App.Form.lib['attachments'] = App.Form.Attachments;
//表单项-下拉框
App.Form.Chosen = function (config){
    this.select = null;
    this.init(config);
    return this.select;
};
App.Form.Chosen.prototype = {
    init: function(config){
        var select = new App.Form.Select(config);
        select.view = function(){//查看
            this.frame.find('.bootstrap-select').hide();
            this.viewEle.text(this.text()).show();
            return this;
        };
        select.edit = function(){
            this.viewEle.hide();
            this.frame.find('.bootstrap-select').show();
            return this;
        };
        select.inputEle.selectpicker({
            liveSearch: config.search
        });
        select.render = function(items, isInit){
            var oldValue = this.valTemp || this.value();
            this.changeOptions(items);
            this.inputEle.selectpicker('refresh');
            if(!isInit){
                var $select = this.frame.find('select').first();
                if($select.find('option[value="'+oldValue+'"]').length > 0){
                    this.change(oldValue);
                }else{
                    this.reset();
                }
            }
        };
        select.validate = function(){
            var rule = this.config.rule;
            if(!rule){
                return true;
            }
            var rs = false;
            if(typeof rule === 'function'){
                rs = this.config.rule(this);
            }else{
                rs = App.util.validator.validate(this.value(), this.config.rule);
            }
            if(!rs){
                this.frame.addClass('error').one('change', function(){
                    $(this).removeClass('error');
                });
            }
            return rs;
        };
        this.select = select;
    }
};
//添加到库
App.Form.lib['chosen'] = App.Form.Chosen;
//表单项-单图上传
App.Form.Img = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    //初始化
    this.init();
};
App.Form.Img.prototype = {
    construtor: App.Form.Img,
    init: function(){
        var self = this;
        this.frame = $([
            '<div>',
            '   <div class="img-upload-container">',
            '       <div class="img-upload-btn-container">',
            '           <i class="fa fa-edit pull-left fa-lg"></i>',
            '           <i class="fa fa-trash-o fa-lg pull-right"></i>',
            '       </div>',
            '       <div class="img-upload-upload-btn">',
            '           点击上传',
            '       </div>',
            '       <div class="img-upload-img-container">',
            '       </div>',
            '       <div class="img-upload-uploadding-container">',
            '           <i class="fa fa-circle-o-notch fa-spin"></i>',
            '           <div class="img-upload-percent">0%</div>',
            '       </div>',
            '       <input class="img-upload-file" type="file" accept="image/*" style="display: none;"/>',
            '       <input class="img-upload-value" type="hidden"/>',
            '   </div>',
            '   <div class="input-comment"></div>',
            '</div>'
        ].join(' '));
        this.frame.find('.img-upload-container').css({
            width: this.config.width || '80px', 
            height: this.config.height || this.config.width
        });
        this.frame.find('.img-upload-value').attr('name', this.name);
        //上传功能初始化
        this.frame.find('.img-upload-upload-btn').click(function(){
            self.frame.find('.img-upload-file').click();
        });
        this.frame.find('.img-upload-btn-container .fa-edit').click(function(){
            self.frame.find('.img-upload-file').click();
        });
        //触发上传
        this.frame.find('.img-upload-file').change(function(){
            if(this.files.length > 0){
                self.upload(this.files[0]);
            }
            this.value = '';
        });
        //删除
        this.frame.find('.img-upload-btn-container .fa-trash-o').click(function(){
            self.change('');
        });
        this.frame.find('.img-upload-value').change(function(){
            self.onChange();
        });
        if(this.config.comment){
            this.frame.find('.input-comment').text(this.config.comment).addClass('show');
        }
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    upload: function(file){
        if(file.type.indexOf('image') !== 0){
            App.util.hint.toast({
                text: '请选择正确的图片文件',
                type: 'warning'
            }).delay(1000);
            return ;
        }
        var self = this;
        //显示进度
        this.frame.find('.img-upload-percent').text('0%');
        this.frame.find('.img-upload-container').addClass('uploadding');
        App.util.uploader.upload({
            file: file,
            progress: function(percent){
                self.frame.find('.img-upload-percent').text(percent+'%');
            },
            success: function(key){
                self.frame.find('.img-upload-container').removeClass('uploadding');
                self.change(key);
                if(self.config.afterUpload){
                    self.config.afterUpload(key);
                }
            }
        }).start();
    },
    change: function(val){//改变
        this.frame.find('.img-upload-img-container img').remove();
        if(!val){
            this.frame.find('.img-upload-container').removeClass('filled');
            this.frame.find('.img-upload-value').val('').change();
            return this;
        }
        this.frame.find('.img-upload-value').val(val).change();
        this.frame.find('.img-upload-container').addClass('filled');
        var img = $('<img>');
        img.attr('src', App.util.formator.fileUrl(val));
        img.dblclick(function(){
            App.imgViewer.view([img.attr('src')]);
        });
        var container = this.frame.find('.img-upload-img-container');
        container.append(img);
        App.util.uploader.resizeImage(img, container.width(), container.height());
        return this;
    },
    view: function(){//查看
        this.frame.find('.img-upload-container').addClass('view');
        return this;
    },
    reset: function(){
        this.frame.find('.img-upload-container').removeClass('filled view uploadding');
        this.frame.find('.img-upload-value').val('').change();
        return this;
    },
    edit: function(){
        this.frame.find('.img-upload-container').removeClass('view');
        return this;
    },
    value: function(){
        return this.frame.find('.img-upload-value').val();
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.find('.img-upload-container').addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.cosnfig.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['img'] = App.Form.Img;
//表单项-多图上传
App.Form.Imgs = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    //初始化
    this.init();
};
App.Form.Imgs.prototype = {
    construtor: App.Form.Imgs,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="imgs-upload-container">',
            '   <div class="clear"></div>',
            '</div>'
        ].join(' '));
        var main = this.getItem(this.config);
        main.frame.addClass('main');
        main.frame.prependTo(this.frame);
        //重写上传
        var file = main.frame.find('.img-upload-file');
        file.prop('multiple', true).unbind('change').change(function(){
            if(this.files.length > 0){
                self.upload(this.files);
            }
        });
        this.frame.find('.img-upload-value').change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    getItem: function(config){
        var self = this;
        var item = new App.Form.Img(config);
        //重写删除
        item.frame.find('.img-upload-btn-container .fa-trash-o').unbind('click').click(function(){
            self.deleteItem(item);
        });
        return item;
    },
    deleteItem: function(target){
        target.frame.remove();
        for(var i in this.items){
            if(target === this.items[i]){
                this.items.splice(i, 1);
            }
        }
    },
    upload: function(files){
        for(var i=0; i<files.length;i++){
            var imgItem = this.getItem(this.config);
            this.frame.find('.main').before(imgItem.frame);
            imgItem.upload(files[i]);
            this.items.push(imgItem);
        }
    },
    change: function(val){//改变
        //清空
        this.reset();
        var valArr = val ? val.split(',') : [];
        for(var i in valArr){
            var imgItem = this.getItem(this.config);
            this.frame.find('.main').before(imgItem.frame);
            imgItem.change(valArr[i]);
            this.items.push(imgItem);
        }
        return this;
    },
    view: function(){//查看
        this.frame.addClass('view');
        for(var i in this.items){
            this.items[i].view();
        }
        return this;
    },
    reset: function(){
        for(var i in this.items){
            this.items[i].frame.remove();
        }
        this.items = [];
        return this;
    },
    edit: function(){
        this.frame.removeClass('view');
        for(var i in this.items){
            this.items[i].edit();
        }
        return this;
    },
    value: function(){
        var vals = [];
        for(var i in this.items){
            vals.push(this.items[i].value());
        }
        return vals.join(',');
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['imgs'] = App.Form.Imgs;
//表单项-选择列表
App.Form.SelectLists = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.selectEle = null;
    this.nameEle = null;
    this.dataEle = null;
    this.valueEle = null;
    this.selector = null;
    this.detailor = null;
    this.dataTable = null;
    this.detail = null;
    this.loaded = false;
    //初始化
    this.init();
};
App.Form.SelectLists.prototype = {
    construtor: App.Form.SelectLists,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="select-lists-container input-group-btn" style="display: inline-block;width: 100%">',
            '   <button class="select-lists-btn btn btn-default form-control" type="button" tabindex="-1" style="width: calc(100% - 33px);margin:0;">',
            '       <span class="select-lists-name pull-left">点击选择</span>',
            '       <span class="pull-right"><span class="caret"></span></span>',
            '   </button>',
            '   <button class="btn btn-default form-control btn-reset" type="button" tabindex="-1" style="width: auto;"><i class="fa fa-trash"></i></button>',
            '   <button class="btn btn-default form-control btn-view" type="button" tabindex="-1" style="width: auto;"><i class="fa fa-eye"></i></button>',
            '   <input class="select-lists-value" name="'+self.name+'" type="hidden"/>',
            '   <input class="select-lists-data" type="hidden"/>',
            '</div>'
        ].join(' '));
        this.dataTable = this.getDataTable();
        this.detail = this.getDetail();
        this.selectEle = this.frame.find('.select-lists-btn').first();
        this.nameEle = this.frame.find('.select-lists-name').first();
        this.valueEle = this.frame.find('.select-lists-value').first();
        this.dataEle = this.frame.find('.select-lists-data').first();
        this.valueEle.change(function(){
            self.onChange();
        });
        this.selectEle.click(function(){
            if(self.frame.hasClass('view')){
                return true;
            }
            if(!self.loaded){
                self.dataTable.dataTable.loadData();
                self.loaded = true;
            }
            self.dataTable.win.show();
        });
        this.frame.find('.btn-reset').click(function(){
            self.reset();
        });
        if(this.detail){
            this.frame.find('button.btn-view').click(function(){
                var id = self.valueEle.val();
                if(!id){
                    return ;
                }
                self.detail.win.show();
                App.util.request.getData({
                    url: self.dataTable.dataTable.url('detail'),
                    data: {id: id},
                    loading: self.detail.form.loading,
                    success: function(data){
                        self.detail.form.change(data);
                        self.detail.form.flushView();
                    }
                });
            });
        }else{
            this.frame.find('button.btn-view').hide();
            this.frame.find('button.select-lists-btn').css({width: '100%'});
        }
        return this;
    },
    setForm: function(form){
        this.form = form;
        //安装弹窗到表单容器
        this.dataTable.win.fix(form.frame);
        if(this.detail){
            this.detail.win.fix(form.frame);
        }
    },
    getDataTable: function(){
        var self = this;
        var win = new App.Win({
            name: 'select-lists-'+self.config.name+'-lists',
            title: self.config.title || '',
            width: self.config.width || '800px',
            lock: true,
            mini: false,
            dragAble: false
        });
        win.hide();
        var config = self.config.dataTable;
        config.container = false;
        config.floatable = false;
        config.dragSort = false;
        config.detail = false;
        config.table.withSelect = false;
        config.table.showId = true;
        var idName = self.config.data_id;
        var titleName = self.config.data_name;
        config.table.columns.push({
            name: '操作',
            width: '70px', 
            value: function(item){
                var btn =  $([
                    '<button class="btn btn-success btn-xs no-margin" type="submit">',
                    '    <i class="fa fa-check"></i> 选择',
                    '</button>'
                ].join(''));
                btn.click(function(){
                    var id = typeof idName === 'function' ? idName(item) : item[idName];
                    var name = typeof titleName === 'function' ? titleName(item) : item[titleName];
                    self.change(id, name, item);
                    win.hide();
                });
                return btn;
            }
        });
        config.menu = [
            {type: 'globe', name: '重置', operation: function(dataTable){
                dataTable.filter.reset();
            }}
        ];
        var dataTable =  new App.DataTable(config);
        dataTable.fix(win.contentContainer);
        return {
            win: win,
            dataTable: dataTable
        };
    },
    getDetail: function(){
        var self = this;
        var config = self.config.detail;
        if(!config){
            return null;
        }
        var win = new App.Win({
            name: 'select-lists-'+self.config.name+'-detail',
            title: config.win ? (config.win.title || '详情') : '详情',
            width: config.win ? (config.win.width || '') : '',
            lock: true,
            mini: false,
            dragAble: false
        });
        win.hide();
        var form = new App.Form(config.form);
        form.fix(win.contentContainer);
        form.view();
        return {
            win: win,
            form: form,
            loading: new App.Loading({
                container: win.contentContainer
            })
        };
    },
    change: function(key, name, data){//改变
        if(data && this.config.data_name && typeof this.config.data_name === 'function'){
            name = this.config.data_name(data);
        }
        this.nameEle.text(name);
        this.dataEle.val(typeof data === 'object' ? JSON.stringify(data) : '');
        this.valueEle.val(key).change();
        return this;
    },
    view: function(){//查看
        if(this.nameEle.text() === '点击选择'){
            this.nameEle.text('');
        }
        this.frame.addClass('view');
        return this;
    },
    reset: function(){
        this.nameEle.text('点击选择');
        this.valueEle.val('').change();
        this.dataEle.val('');
        return this;
    },
    edit: function(){
        if(this.nameEle.text() === ''){
            this.nameEle.text('点击选择');
        }
        this.frame.removeClass('view');
        return this;
    },
    value: function(){
        return this.valueEle.val();
    },
    enable: function(){
        this.selectEle.prop('disabled', false);
        return this;
    },
    disable: function(){
        this.selectEle.prop('disabled', true);
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error');
            this.valueEle.one('change', function(){
                self.frame.removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.win.destroy();
        this.dataTable.destroy();
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['selectLists'] = App.Form.SelectLists;
//表单项-标签输入
App.Form.Tags = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    //初始化
    this.init();
};
App.Form.Tags.prototype = {
    construtor: App.Form.Tags,
    init: function(){
        var self = this;
        var title = this.config.title ? '点击输入'+this.config.title : '';
        this.frame = $([
            '<div class="tags-container">',
            '   <input name="'+self.name+'" type="text" class="form-control" value="" placeholder="'+title+'"/>',
            '</div>'
        ].join(' '));
        this.inputEle = this.frame.find('input').first();
        this.inputEle.tagsinput({
            //'height':'auto', //设置高度
            //'width':'300px',  //设置宽度
            interactive: true, //是否允许添加标签，false为阻止
            defaultText: '点击输入', //默认文字
            removeWithBackspace : true, //是否允许使用退格键删除前面的标签，false为阻止
            minChars : 2, //每个标签的小最字符
            maxChars : 30, //每个标签的最大字符，如果不设置或者为0，就是无限大
            maxCount: 20
        });
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    change: function(val){//改变
        this.inputEle.tagsinput('removeAll');
        this.inputEle.tagsinput('add', val);
        return this;
    },
    view: function(){//查看
        this.frame.addClass('view');
        return this;
    },
    reset: function(){
        this.inputEle.tagsinput('removeAll');
        return this;
    },
    edit: function(){
        this.frame.removeClass('view');
        return this;
    },
    value: function(){
        return this.inputEle.val();
    },
    enable: function(){
        this.frame.removeClass('disable');
        return this;
    },
    disable: function(){
        this.frame.addClass('disable');
        return this;
    },
    validate: function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error');
            this.inputEle.one('change', function(){
                self.frame.removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    },
    destroy: function(){
        this.frame.remove();
    }
};
//添加到库
App.Form.lib['tags'] = App.Form.Tags;
//表单项-视频上传
App.Form.Video = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.filename = '';
    this.frame = null;
    //初始化
    this.init();
};
App.Form.Video.prototype = {
    construtor: App.Form.Video,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="video-upload-container view">',
            '    <div class="add-page">',
            '        <div class="btn-container">',
            '            <a class="upload-btn btn btn-info btn-xs m-right-md">上传</a>',  
            '            <a class="select-btn btn btn-info btn-xs">选择</a>',
            '        </div>',
            '        <i class="fa fa-times-circle fa-lg close-btn"></i>',
            '    </div>',
            '    <div class="edit-page">',
            '        <div class="btn-container">',
            '            <i class="edit-btn fa fa-edit pull-left fa-lg"></i>',   
            '            <i class="drop-btn fa fa-trash-o fa-lg pull-right"></i>',    
            '        </div>',
            '        <div class="video-container">',
            '            <video src="#" controls preload="none" width="100%" height="100%" poster=""></video>',
            '        </div>',
            '    </div>',
            '    <div class="uploadding-page">',
            '        <div class="progress">',
            '            <div class="progress-bar progress-bar-success" style="width: 12%;"></div>',
            '            <div class="percent">12%</div>',
            '        </div>',
            '    </div>',
            '    <input class="video-upload-file" type="file" accept="video/*" style="display: none;">',
            '    <input class="video-upload-value" type="hidden">',
            '</div>'
        ].join(''));
        this.frame.css({
            width: this.config.width || '320', 
            height: this.config.height || '180px'
        });
        this.frame.find('.video-upload-value').attr('name', this.name);
        //上传功能初始化
        this.frame.find('.upload-btn').click(function(){
            self.frame.find('.video-upload-file').click();
        });
        this.frame.find('.video-upload-file').change(function(){
            if(this.files.length > 0){
                var file = this.files[0];
                if(self.config.beforeUpload){
                    self.config.beforeUpload(file, self);
                }else{
                    self.upload(file);
                }
                this.value = '';
            }
        });
        //编辑
        this.frame.find('.edit-btn').click(function(){
            self.frame.find('.edit-page').hide();
            self.frame.find('.uploadding-page').hide();
            self.frame.find('.add-page').addClass('edit').show();
        });
        this.frame.find('.add-page .close-btn').click(function(){
            self.frame.find('.add-page').hide();
            self.frame.find('.edit-page').show();
        });
        //删除
        this.frame.find('.drop-btn').click(function(){
            self.change('');
        });
        this.frame.find('.video-upload-value').change(function(){
            self.onChange();
        });
        return this;
    },
    setForm: function(form){
        this.form = form;
    },
    upload: function(file){
        if(file.type.indexOf('video') !== 0){
            App.util.hint.toast({
                text: '请选择正确的视频文件',
                type: 'warning'
            }).delay(1000);
            return ;
        }
        this.filename = file.name;
        var self = this;
        //切换页面
        this.frame.find('.add-page').hide();
        this.frame.find('.edit-page').hide();
        this.frame.find('.uploadding-page').show();
        //显示进度
        this.frame.find('.uploadding-page .progress-bar').css('width', '0%');
        this.frame.find('.uploadding-page .percent').text('0%');
        App.util.uploader.upload({
            file: file,
            progress: function(percent){
                self.frame.find('.uploadding-page .progress-bar').css('width', percent+'%');
                self.frame.find('.uploadding-page .percent').text(percent+'%');
            },
            success: function(key, avinfo){
                self.frame.find('.uploadding-page').hide();
                self.frame.find('.add-page').hide();
                self.frame.find('.edit-page').show();
                self.change(key);
                if(self.config.afterUpload){
                    self.config.afterUpload(key);
                }
            }
        }).start();
    },
    change: function(val){//改变
        if(!val){
            //切换页面
            this.frame.find('.edit-page').hide();
            this.frame.find('.uploadding-page').hide();
            this.frame.find('.add-page').removeClass('edit').show();
            this.frame.find('.video-upload-value').val('').change();
            this.frame.find('.video-container video').attr('src', '#');
            return this;
        }
        //切换页面
        this.frame.find('.add-page').hide();
        this.frame.find('.uploadding-page').hide();
        this.frame.find('.edit-page').show();
        this.frame.find('.video-upload-value').val(val).change();
        this.frame.find('.video-container video').attr('src', App.util.formator.fileUrl(val));
        return this;
    },
    view: function(){//查看
        //切换页面
        this.frame.find('.add-page').hide();
        this.frame.find('.uploadding-page').hide();
        this.frame.find('.edit-page').hide();
        if(this.value().length > 0){
            this.frame.find('.edit-page').show();
        }
        this.frame.addClass('view');
        return this;
    },
    reset: function(){
        this.change('');
        return this;
    },
    edit: function(){
        this.frame.removeClass('view');
        return this;
    },
    value: function(){
        return this.frame.find('.video-upload-value').val();
    },
    enable: function(){
        return this;
    },
    disable: function(){
        return this;
    },
    validate: function(){
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error').one('change', function(){
                $(this).removeClass('error');
            });
        }
        return rs;
    },
    onChange: function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    }
};
//添加到库
App.Form.lib['video'] = App.Form.Video;
//表单项-输入框
App.Form.DateBase = function (config){
    this.config = config;
    this.name = this.config.name;
    this.form = null;
    this.frame = null;
    this.inputEle = null;
    this.inputGroup = null;
    this.viewEle = null;
    this.init = function(){
        var self = this;
        var title = this.config.title ? '点击选择'+this.config.title : '';
        this.frame = $([
            '<div class="date-pick-container">',
            '    <div class="input-group">',
            '        <input name="'+self.name+'" type="text" class="form-control" placeholder="'+title+'"/>',
            '        <span class="input-group-addon"><i class="fa fa-calendar"></i></span>',
            '    </div>',
            '   <span class="form-static" style="display: none;"></span>',
            '</div>'
        ].join(' '));
        this.inputGroup = this.frame.find('> .input-group').first();
        this.inputEle = this.frame.find('input.form-control').first();
        this.viewEle = this.frame.find('.form-static').first();
        this.inputEle.blur(function(){
            self.onChange();
        });
        this.inputEle.change(function(){
            self.onChange();
        });
        return this;
    };
    this.setForm = function(form){
        this.form = form;
    };
    this.change = function(val){//改变
        var value = val ? App.util.formator.date(val) : '';
        this.inputEle.val(value).change();
        return this;
    };
    this.view = function(){//查看
        this.inputGroup.hide();
        this.viewEle.text(this.inputEle.val()).show();
        return this;
    };
    this.reset = function(){
        this.inputEle.val('').change();
        return this;
    };
    this.edit = function(){
        this.viewEle.hide();
        this.inputGroup.show();
        return this;
    };
    this.value = function(){
        var value = this.inputEle.val();
        return value ? App.util.formator.timestamp(value) : '';
    };
    this.enable = function(){
        this.inputEle.prop('disabled', false);
        return this;
    };
    this.disable = function(){
        this.inputEle.prop('disabled', true);
        return this;
    };
    this.validate = function(){
        var self = this;
        var rule = this.config.rule;
        if(!rule){
            return true;
        }
        var rs = false;
        if(typeof rule === 'function'){
            rs = this.config.rule(this);
        }else{
            rs = App.util.validator.validate(this.value(), this.config.rule);
        }
        if(!rs){
            this.frame.addClass('error');
            this.inputEle.one('change', function(){
                self.frame.removeClass('error');
            });
        }
        return rs;
    };
    this.onChange = function(){
        if(this.config.onChange){
            this.config.onChange(this);
        }
    };
    this.destroy = function(){
        this.frame.remove();
    };
    //初始化
    this.init();
};
//表单项-年
App.Form.Date = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY-MM-DD',
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.date(val) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['date'] = App.Form.Date;
//表单项-年
App.Form.DateHour = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY-MM-DD HH', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.dateTime(val).substr(0, 13) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['dateHour'] = App.Form.DateHour;
//表单项-年
App.Form.DateMin = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY-MM-DD HH:mm', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.dateTime(val).substr(0, 16) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['dateMin'] = App.Form.DateMin;
//表单项-年
App.Form.DateMonth = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY-MM', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.dateTime(val).substr(0, 7) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['dateMonth'] = App.Form.DateMonth;
//表单项-年
App.Form.DateTime = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY-MM-DD HH:mm:ss', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.dateTime(val) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['dateTime'] = App.Form.DateTime;
//表单项-年
App.Form.DateYear = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'YYYY', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'auto'
        }
    });
    this.change =  function(val){
        var value = val ? App.util.formator.dateTime(val).substr(0, 4) : '';
        this.inputEle.val(value).change();
        return this;
    };
};
//添加到库
App.Form.lib['dateYear'] = App.Form.DateYear;
//表单项-年
App.Form.Time = function (config){
    App.Form.DateBase.call(this, config);
    this.inputEle.datetimepicker({
        format: 'HH:mm:ss', 
        locale: 'zh-cn',
        widgetPositioning: {
            horizontal: 'left',
            vertical: 'bottom'
        }
    });
    this.inputGroup.find('.input-group-addon i').removeClass('fa-calendar').addClass('fa-clock-o');
    this.change =  function(val){
        this.inputEle.val(App.util.formator.time(val)).change();
        return this;
    };
    this.value = function(){
        var value = this.inputEle.val();
        return value ? App.util.formator.timeToSeconds(value) : '';
    };
};
//添加到库
App.Form.lib['time'] = App.Form.Time;
App.DataTable = function (config){
    //参数配置初始化
    config.floatable = config.floatable === false ? false : true;
    config.paginator = config.paginator === false ? false : true;
    config.oplog = config.oplog === false ? false : {};
    //基础
    this.config = config;
    //属性
    this.id = null;
    this.param = null;
    this.frame = null;
    this.table = null;
    this.menu = null;
    this.filter = null;
    this.paginator = null;
    this.floatable = null;
    this.dragSort = null;
    this.detail = null;
    this.oplog = null;
    this.after = null;
    this.loading = null;
    this.wins = new App.Stack();
    //初始化
    this.init();
};
App.DataTable.prototype = {
    constructor: App.DataTable,
    init: function(){
        var self = this;
        this.id = this.config.baseUrl.replace('/', '-')+"-"+this.config.dataUrl;
        //初始化组件
        this.frame = $([
            '<div class="data-table-container">',
            '   <div class="header clearfix">',
            '   </div>',
            '</div>'
        ].join(''));
        this.param = new App.DataTable.Param(this);
        this.after = this.getAfterStack();
        this.table = new App.DataTable.Table(this, this.config.table);
        if(this.config.params){
            var params = this.config.params;
            for(var i in params){
                var key = params[i]['key'] ? params[i]['key'] : params[i][0];
                var value = params[i]['value'] ? params[i]['value'] : params[i][1];
                this.param.set(key, value);
            }
        }
        if(this.config.menu){
            this.menu = new App.DataTable.Menu(this, this.config.menu);
        }
        if(this.config.filter){
            this.filter = new App.DataTable.Filter(this, this.config.filter);
        }
        if(this.config.paginator){
            this.paginator = new App.DataTable.Paginator(this, this.config.paginator);
        }
        if(this.config.floatable){
            //this.floatable = new App.DataTable.Floatable(this, this.config.floatable);
        }
        if(this.config.dragSort){
            this.dragSort = new App.DataTable.DragSort(this, this.config.dragSort);
        }
        if(this.config.detail){
            this.detail = new App.DataTable.Detail(this, this.config.detail);
        }
        if(this.config.oplog){
            this.oplog = new App.DataTable.Oplog(this, this.config.oplog);
        }
        //初始化之后的操作
        if(this.config.after){
            this.config.after(this);
        }
        this.after.fire();
        //添加组件
        if(this.config.container){
            this.fix(this.config.container);
        }
        //最小宽度限制
        var minWidthFilter = this.filter ? this.filter.getMinWidth() : 0;
        var minWidthMenu = this.menu ? this.menu.getGlobleMinWidth() : 0;
        var minWidthHeader = Math.ceil(minWidthFilter + minWidthMenu + 30);
        var minWidthTable = this.table.getMinWidth();
        this.frame.css({'min-width': (minWidthHeader > minWidthTable ? minWidthHeader : minWidthTable)+'px'});
    },
    fix: function(container){
        this.frame.appendTo(container);
        this.wins.each(function(item){
            item.fix(container);
        });
        this.loading = new App.Loading({container: container});
    },
    loadData: function(resetFilter){
        var self = this;
        if(resetFilter){//重置过滤器
            if(this.filter){
                this.filter.reset();
            }
            if(this.paginator){
                this.paginator.currentPage = 1;
            }
        }
        var params = App.util.copy(this.param.all());
        var filterParam = this.filter ? this.filter.getData() : {};
        for(var i in filterParam){
            params[i] = filterParam[i];
        }
        if(this.paginator){
            params['page'] = this.paginator.currentPage;
            params['pageSize'] = this.paginator.pageSize;
        }
        App.util.request.getData({
            url: self.url(self.config.dataUrl),
            data: params,
            loading: self.loading,
            success: function(data){
                self.table.flushItmes(data.data);
                if(self.menu){
                    self.menu.flush();
                }
                if(self.paginator){
                    self.paginator.flush(data.count);
                }
                //后置处理
                if(self.config.afterLoadData){
                    self.config.afterLoadData(self, data.data);
                }
            }
        });
        return this;
    },
    postData: function(config){
        var self = this;
        App.util.request.postData({
            url: self.url(config.url),
            data: (function(){
                var param = App.util.copy(self.param.all());
                for(var i in config.data){
                    param[i] = config.data[i];
                }
                return param;
            })(),
            loading: config.loading || self.loading,
            success: function(data){
                if(config.alert){
                    App.util.hint.toast({
                        type: 'ok',
                        text: config.alert
                    }).delay(500, function(){
                        if(config.reload){
                            self.loadData();
                        }
                    });
                } else if(config.reload){
                    self.loadData();
                }
                if(config.after){
                    config.after(data);
                }
                //执行实体修改后的操作
                if(self.config.afterChange){
                    self.config.afterChange(config, self);
                }
            },
            error: config.error || null
        });
        return this;
    },
    url: function(url){
        return url.indexOf('/') === 0 ? url : this.config.baseUrl+'/'+url;
    },
    condition: {
        match: function(item, conditions){
            if(!conditions || conditions.length < 1){
                return true;
            }
            for(var i in conditions){
                var condition = conditions[i];
                if(typeof condition === 'function'){
                    if(!condition(item)){
                        return false;
                    }
                    continue;
                }
                if(condition.length === 2){
                    condition = [condition[0], '=', condition[1]];
                }else if(condition.length !== 3){
                    continue;
                }
                var field = condition[0];
                var operator = condition[1]+'';
                var expected = condition[2]+'';
                var value = item[field]+'';
                if(value === undefined){
                    return false;
                }
                switch (operator){
                    case '=' :
                        if(value !== expected){
                            return false;
                        }
                        break;
                    case '>' :
                        if(value <= expected){
                            return false;
                        }
                        break;
                    case '<' :
                        if(value >= expected){
                            return false;
                        }
                        break;
                    case '!=' :
                        if(value === expected){
                            return false;
                        }
                        break;
                    case 'in' :
                        if(typeof expected === 'string'){
                            expected = expected.split(',');
                        }
                        if(expected.indexOf(value) === false){
                            return false;
                        }
                        break;
                    default:
                        return false;
                }
            }
            return true;
        }
    },
    sleep: function(){
        if(this.floatable){
            this.floatable.sleep();
        }
    },
    wakeup: function(){
        if(this.floatable){
            this.floatable.wakeup();
        }
    },
    getAfterStack: function(){
        var self = this;
        return {
            items: {},
            add: function(name, callback){
                this.items[name] = callback;
            },
            remove: function(name){
                delete this.items[name];
            },
            fire: function(){
                for(var i in this.items){
                    this.items[i](self);
                }
            }
        };
    },
    destroy: function(){
        this.sleep();
        this.param.destroy();
        this.table.destroy();
        this.menu ? this.menu.destroy() : '';
        this.filter ? this.filter.destroy() : '';
        this.paginator ? this.paginator.destroy() : '';
        this.floatable ? this.floatable.destroy() : '';
        this.dragSort ? this.dragSort.destroy() : '';
        this.detail ? this.detail.destroy() : '';
        this.oplog ? this.oplog.destroy() : '';
        this.frame.remove();
    }
};
App.DataTable.Param = function (dataTable){
    this.dataTable = dataTable;
    this.data = {};
};
App.DataTable.Param.prototype = {
    constructor: App.DataTable.Param,
    set: function(key, val){
        this.data[key] = val;
    },
    get: function(key){
        return this.data[key];
    },
    all: function(){
        return this.data;
    },
    exist: function(key){
        return this.data.hasOwnProperty(key);
    },
    each: function(callback){
        var items = this.stack;
        for(var i in items){
            callback(i, items[1]);
        }
    },
    remove: function(key){
        delete this.data[key];
    },
    destroy: function(){
        delete this.data;
    }
};
App.DataTable.Table = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.config.withSelect = this.config.withSelect === false ? false : true;
    this.config.showId = this.config.showId === false ? false : true;
    if(App.size === 'sm'){
        this.config.showId = false;
    }
    this.frame = null;
    this.minWidth = 1;
    this.init();
};
App.DataTable.Table.prototype = {
    constructor: App.DataTable.Table,
    init: function(){
        this.frame = $([
            '<table class="table table-hover table-striped no-margin data-table">',
            '    <thead>',
            '    </thead>',
            '    <tbody>',
            '    </tbody>',
            '</table>'
        ].join(''));
        this.initHead();
        this.initSelect();
        this.initSelectAll();
        this.frame.appendTo(this.dataTable.frame);
    },
    initHead: function(){
        var columns = this.config.columns;
        var headTr = $('<tr></tr>');
        //ID
        if(this.config.withSelect || this.config.showId){
            var width = '';
            if(this.config.showId && this.config.withSelect){
                width = '81px';
            }else if(this.config.showId){
                width = '61px';
            }else if(this.config.withSelect){
                width = '29px';
            }
            $([
                '<th class="column-id" style="text-align: left;" width="'+width+'">',
                '    <label>',
                '        '+(this.config.withSelect ? '<input type="checkbox" class="chk-all">' : '')+(this.config.showId ? ' ID' : ''),
                '    </label>',
                '</th>'
            ].join('')).appendTo(headTr);
            this.minWidth += parseInt(width);
        }
        //普通列
        for(var i in columns){
            var th = null;
            if(i+'' === this.config.main+'' || columns[i].value === this.config.main){
                th = this.getTh(columns[i].name);
                th.addClass('column-main');
            }else{
                th = this.getTh(columns[i].name, columns[i].width);
            }
            this.minWidth += columns[i].width ? parseInt(columns[i].width) : 150;
            th.appendTo(headTr);
        }
        headTr.appendTo(this.frame.find('thead'));
    },
    initSelect: function(){
        if(this.config.withSelect){
            var self = this;
            //单选效果
            self.frame.find('.chk-row').each(function(){
                $(this).unbind('change').change(function(){
                    if($(this).prop('checked')){
                        $(this).parent().parent().parent().addClass('selected');
                    }else{
                        $(this).parent().parent().parent().removeClass('selected');
                    }
                });
            });
        }
        return this;
    },
    initSelectAll: function(){
        if(this.config.withSelect){
            var self = this;
            //全选
            self.frame.find('.chk-all').unbind('click').click(function(){
                var checked = $(this).prop('checked');
                self.frame.find('.chk-row').not(':disabled').each(function(){
                    $(this).prop('checked', checked).change();
                });
            });
        }
        return this;
    },
    flushItmes: function(data){
        var tbody = this.frame.find('tbody');
        //清空原有数据
        tbody.find('tr').remove();
        this.appendItmes(data);
    },
    appendItmes: function(data){
        if(this.config.getItems){
            return this.config.getItems(data, this);
        }
        var id = this.config.id;
        var columns = this.config.columns;
        var tbody = this.frame.find('tbody');
        for(var i in data){
            var item = data[i];
            var tr = $('<tr></tr>').appendTo(tbody);
            //ID
            if(this.config.withSelect || this.config.showId){
                $([
                    '<td class="column-id" style="text-align: left;">',
                    '    <label>',
                    '        '+(this.config.withSelect ? '<input type="checkbox" class="chk-row" name="ids[]" value="'+item[id]+'">' : ''),
                    '        '+(this.config.showId ? item[id] : ''),
                    '    </label>',
                    '</td>'
                ].join('')).appendTo(tr);
            }
            //普通列
            for(var j in columns){
                var td = this.getTd(this.getValue(columns[j].value, item), columns[j].style);
                if(j+'' === this.config.main+'' || columns[j].value === this.config.main){
                    td.addClass('column-main');
                }
                td.appendTo(tr);
            }
            tr.attr('data-data', JSON.stringify(item));
        }
        this.initSelect();
        if(this.dataTable.dragSort){
            this.dataTable.dragSort.flush();
        }
    },
    getValue: function(tpl, item){
        var self = this;
        return typeof tpl === 'function' ? tpl(item, self.dataTable) : item[tpl];
    },
    getMinWidth: function(){
        return this.minWidth;
    },
    getTh: function(value, width){
        return $('<th class="text-center" '+(width ? 'width="'+width+'"' : '')+'>'+value+'</th>');
    },
    getTd: function(value, style){
        var td = $('<td '+(style ? 'style="'+style+'"' : '')+'></td>');
        if(typeof value === 'object'){
            td.append(value);
        }else{
            td.text(value);
        }
        return td;
    },
    getSelectedIds: function(){
        var ids = [];
        this.frame.find('.chk-row').each(function(){
            if($(this).prop('checked')){
                ids.push($(this).attr('value'));
            }
        });
        return ids;
    },
    getSelectIds: function(){
        var ids = [];
        this.frame.find('.chk-row').each(function(){
            ids.push($(this).attr('value'));
        });
        return ids;
    },
    destroy: function(){
        this.frame.remove();
    }
};
App.DataTable.Detail = function (dataTable, config){
    this.config = config;
    this.config.dataUrl = this.config.dataUrl || 'detail';
    this.config.addUrl = this.config.addUrl || 'create';
    this.config.editUrl = this.config.editUrl || 'update';
    this.dataTable = dataTable;
    this.form = null;
    this.win = null;
    this.loading = null;
    this.status = 0;
    this.init();
};
App.DataTable.Detail.prototype = {
    constructor: App.DataTable.Detail,
    init: function(){
        var self = this;
        var winConfig = self.config.win || {};
        winConfig.name = self.dataTable.id+'-detail';
        self.win = new App.Win(winConfig);
        //设置表单
        self.form = new App.Form(self.config.form);
        self.form.fix(self.win.contentContainer);
        //正在加载
        self.loading = new App.Loading({
            container: self.win.contentContainer
        });
        //设置按钮
        self.win.setBtns([(function(){
            var cancelBtn = $('<a class="btn btn-danger btn-xs m-right-sm">取消</a>');
            cancelBtn.click(function(){
                self.win.hide();
            });
            return cancelBtn;
        })(), (function(){
            var saveBtn = $('<a class="btn btn-success btn-xs">保存</a>');
            saveBtn.click(function(){
                if(!self.form.validate()){
                    return false;
                }
                if(self.status === 2){
                    self.dataTable.postData({
                        url: self.config.addUrl,
                        data: self.form.getData(),
                        alert: '保存成功！',
                        reload: true,
                        loading: self.loading,
                        after: function(){
                            self.win.hide();
                        }
                    });
                }else if(self.status === 3){
                    self.dataTable.postData({
                        url: self.config.editUrl,
                        data: self.form.getData(),
                        alert: '保存成功！',
                        reload: true,
                        loading: self.loading,
                        after: function(){
                            self.win.hide();
                        }
                    });
                }
            });
            return saveBtn;
        })()]);
        self.win.hide();
        self.dataTable.wins.add(self.win, 'detail');
    },
    change: function(data, after){
        var self = this;
        var primaryKeyName = this.dataTable.config.table.id;
        var primaryKey = data[primaryKeyName];
        var url = this.dataTable.url(this.config.dataUrl)+'?'+primaryKeyName+'='+primaryKey;
        self.form.fillFromUrl(url, after);
        return this;
    },
    add: function(){
        this.status = 2;
        this.win.setTitle('添加');
        this.form.add().reset();
        this.win.show().showBtns();
        return this;
    },
    view: function(data){
        this.status = 1;
        this.win.setTitle('详情');
        this.form.view();
        this.win.show().hideBtns();
        this.change(data, function(form){
            form.flushView();
        });
        return this;
    },
    edit: function(data){
        this.status = 3;
        this.win.setTitle('编辑');
        this.form.edit();
        this.win.show().showBtns();
        this.change(data);
        return this;
    },
    destroy: function(){
        this.form.destroy();
        this.win.destroy();
        this.loading.destroy();
    }
};
App.DataTable.DragSort = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.table = this.dataTable.table;
    this.container = this.dataTable.frame;
    this.enable = true;
    this.init();
};
App.DataTable.DragSort.prototype = {
    constructor: App.DataTable.DragSort,
    init: function(){
        if(this.config.permission && !App.permission.check(this.config.permission)){
            this.enable = false;
        }
        this.iniHead();
    },
    flush: function(){
        this.initBtns();
    },
    hide: function(){
        this.table.frame.find('.column-sort').hide();
    },
    show: function(){
        this.table.frame.find('.column-sort').show();
    },
    iniHead: function(){
        if(!this.enable){
            return ;
        }
        var table = this.table.frame;
        //表头
        var th = this.table.getTh('排序', '41px');
        th.addClass('column-sort');
        table.find('thead tr th.column-main').before(th);
        this.table.minWidth += parseInt(41);
    },
    initBtns: function(){
        if(!this.enable){
            return ;
        }
        var self = this;
        var table = this.table.frame;
        var field = this.config.field || 'sort';
        table.find('tbody tr').each(function(){
            var tr = $(this);
            var td = self.table.getTd('');
            td.addClass('column-sort');
            tr.find('td.column-main').before(td);
            var data = JSON.parse(tr.attr('data-data'));
            var sortNum = parseInt(data[field]);
            var idName = self.config.id ? self.config.id: self.table.config.id;
            var id = data[idName];
            if(sortNum < 10000){//已置顶
                $(this).addClass('sorted');
                td.append(self.getSortedBtn(id, sortNum));
            }else if(self.dataTable.condition.match(data, self.config.conditions)){//可置顶
                td.append(self.getAppendBtn(id));
            }
        });
    },
    getSortedBtn: function(id, sort){
        var self = this;
        var btnContainer = $([
            '<div class="sort-sorted-menu">',
            '    <a class="sort-move-btn" href="javascript:void(0);" title="拖动以排序">',
            '        <span>'+sort+'</span>',
            '    </a>',
            '    <div class="sort-menus">',
            '        <a class="sort-cancel-btn" href="javascript:void(0);" title="取消置顶">',
            '            <i class="fa fa-times-circle-o" aria-hidden="true"></i>',
            '        </a>',
            '    </div>',
            '</div>'
        ].join(''));
        //取消按钮
        var cancelBtn = btnContainer.find('.sort-cancel-btn');
        cancelBtn.click(function(){
            self.postData('cancel', id);
        });
        //拖动
        var moveBtn = btnContainer.find('.sort-move-btn');
        self.dragAble(moveBtn);
        return btnContainer;
    },
    getAppendBtn: function(id){
        var self = this;
        var btnContainer = $([
            '<div class="sort-unsorted-menu">',
            '    <a class="sort-append-btn" href="javascript:void(0);" title="置顶">',
            '        <i class="fa fa-arrow-circle-o-up"></i>',
            '    </a>',
            '</div>'
        ].join(''));
        var btn = btnContainer.find('.sort-append-btn');
        btn.click(function(){
            self.postData('append', id);
        });
        return btnContainer;
    },
    dragAble: function(handle){
        var self = this;
        handle.off("mousedown").mousedown(function(e) {
            // 排除非左击
            if(e.which !== 1){
                return; 
            }
            //阻止选中文本
            e.preventDefault();
            var containerOffset = self.container.offset();
            //当前行初始化
            var currentTr = $(this).parent().parent().parent();
            var currentOffset = currentTr.offset();
            var i = 0;
            self.container.find('thead tr').first().children().each(function(){
                var width = $(this).attr('width') || $(this).outerWidth(true);
                var j = 0;
                currentTr.children().each(function(){
                    if(i === j && width){
                        $(this).attr('width', width);
                    }
                    j++;
                });
                i++;
            });
            //占位行
            var holder = $('<tr class="holder"></tr>').css({"height":currentTr.outerHeight(true)});
            currentTr.before(holder);
            //浮动当前行
            currentTr.css({
                top: currentOffset.top - containerOffset.top, 
                left: currentOffset.left - containerOffset.left,
                width: currentTr.outerWidth()
            });
            currentTr.addClass('temp');
            var y = e.pageY;
            var h = currentTr.height();
            // 绑定mousemove事件
            $(document).mousemove(function(e){
                e.preventDefault();
                // 移动选中块
                currentTr.css({"top": currentOffset.top - containerOffset.top + e.pageY - y});
                //移动虚线框
                var offset = currentTr.offset().top - holder.offset().top;
                if(Math.abs(offset) > h){
                    if(offset > 0 && holder.next('.sorted')){//下移
                        holder.next('.sorted').after(holder);
                    }else if(offset < 0 && holder.prev('.sorted')){//上移
                        holder.prev('.sorted').before(holder);
                    }
                }
            });
            // 绑定mouseup事件
            $(document).mouseup(function() {
                //解除鼠标移动事件
                $(document).off('mouseup').off('mousemove');
                // 拖拽回位，并删除虚线框
                holder.after(currentTr);
                holder.remove();
                holder = null;
                currentTr.removeClass('temp');
                currentTr.find('td').removeAttr('width');
                var prev = currentTr.prev('tr.sorted');
                var next = currentTr.next('tr.sorted');
                var idName = self.config.id ? self.config.id: self.dataTable.table.config.id;
                var currentData = JSON.parse(currentTr.attr('data-data'));
                var currentId = currentData[idName];
                if(prev.length > 0){
                    var prevData = JSON.parse(prev.attr('data-data'));
                    var prevId = prevData[idName];
                    self.postData('after', currentId, prevId);
                }else if(next.length > 0){
                    var nextData = JSON.parse(next.attr('data-data'));
                    var nextId = nextData[idName];
                    self.postData('before', currentId, nextId);
                }
            });
        });
    },
    postData: function(operation, selfId, targetId){
        var self = this;
        this.dataTable.postData({
            url: this.config.url,
            data: (function(){
                var data = {
                    operation: operation,
                    id: selfId,
                    target_id: targetId
                };
                if(self.config.append){
                    var appends = {};
                    if(typeof self.config.append === 'function'){
                        appends = self.config.append(self.dataTable);
                    }else{
                        appends = self.config.append;
                    }
                }
                for(var i in appends){
                    data[i] = appends[i];
                }
                return data;
            })(),
            alert: '操作成功！',
            reload: true,
            error: function(){
                self.dataTable.loadData();
            }
        });
    },
    destroy: function(){
    }
};
App.DataTable.Filter = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.models = [];
    this.frame = null;
    this.minWidth = 33;
    this.init();
};
App.DataTable.Filter.prototype = {
    constructor: App.DataTable.Filter,
    init: function(){
        this.frame = $([
            '<div class="filter clearfix pull-left">',
            '    <form class="filter-form form-inline">',
            '        <button type="submit" class="refresh-btn btn btn-default btn-sm no-margin">',
            '            <i class="fa fa-search"></i>',
            '        </button>',
            '    </form>',
            '</div>'
        ].join(''));
        this.initSubmit();
        this.initConditions();
        this.dataTable.frame.find('.header').prepend(this.frame).show();
    },
    getMinWidth: function(){
        return this.minWidth;
    },
    initSubmit: function(){
        var self = this;
        this.frame.find('.filter-form').submit(function(){
            self.flushData();
            if(self.dataTable.paginator){
                self.dataTable.paginator.currentPage = 1;
            }
            self.dataTable.loadData();
            return false;
        });
    },
    initConditions: function(){
        var config = this.config;
        var refreshBtn = this.frame.find('.refresh-btn');
        for(var i in config){
            var item = config[i];
            switch (item.type){
                case 'keywords' :
                    refreshBtn.before(this.getKeywordsFilter(item['name'], item['title'], item['width']));
                    this.minWidth += parseInt(item['width'] || 130) + 3;
                    break;
                case 'timeRange' :
                    refreshBtn.before(this.getTimeRangeFilter());
                    this.minWidth += 220 + 3;
                    break;
                case 'hidden' :
                    refreshBtn.before(this.getHiddenFilter(item['name']));
                    break;
                case 'input' :
                    refreshBtn.before(this.getInputFilter(item['name'], item['title'], item['width']));
                    this.minWidth += parseInt(item['width']) + 3;
                    break;
                case 'select' :
                    refreshBtn.before(this.getSelectFilter(item['name'], item['title'], item['width'], item['items'], item['item_key'], item['item_name'], item['filter']));
                    this.minWidth += parseInt(item['width']) + 3;
                    break;
            }
        }
    },
    getKeywordsFilter: function(name, title, width){
        return this.getInputFilter(name || 'keywords', title || '关键词', width || '130px');
    },
    getTimeRangeFilter: function(){
        var frame =  $([
            '<div class="filter-item input-group input-group-sm" style="width: 220px;">',
            '    <input class="form-control" name="begin_time" placeholder="开始时间" type="text">',
            '    <span class="input-group-addon" style="border-left: none;border-right: none;">-</span>',
            '    <input class="form-control" name="end_time" placeholder="结束时间" type="text">',
            '</div>'
        ].join(''));
        frame.find('input[name="begin_time"]').datetimepicker({
            format: 'YYYY-MM-DD',
            locale: 'zh-cn',
            widgetPositioning: {
                horizontal: 'left',
                vertical: 'bottom'
            }
        });
        frame.find('input[name="end_time"]').datetimepicker({
            format: 'YYYY-MM-DD',
            locale: 'zh-cn',
            widgetPositioning: {
                horizontal: 'right',
                vertical: 'bottom'
            }
        });
        var i = 0;
        frame.find('.datetimepicker').each(function(){
            i++;
            if(i === 1){
                $(this).css({left: '0'});
            }else if(i === 2){
                $(this).css({left: 'auto', right: '0'});
            }
        });
        return frame;
    },
    getHiddenFilter: function(name){
        return $([
            '<div class="filter-item input-group input-group-sm" style="display:none">',
            '    <input class="form-control" name="'+name+'" type="hidden">',
            '</div>'
        ].join(''));
    },
    getInputFilter: function(name, title, width){
        return $([
            '<div class="filter-item input-group input-group-sm" style="width: '+width+';">',
            '    <input class="form-control" name="'+name+'" placeholder="'+(title || '')+'" type="text">',
            '</div>'
        ].join(''));
    },
    getSelectFilter: function(name, title, width, items, item_key, item_name, filter){
        var selectContainer = $([
            '<div class="filter-item input-group input-group-sm" style="width: '+width+';">',
            '    <select name="'+name+'" class="form-control">',
            '        <option value="">'+(title || '')+'</option>',
            '    </select>',
            '</div>'
        ].join(''));
        var select = selectContainer.find('select');
        select.selectpicker({});
        var render = function(items){
            if(filter){
                items = filter(App.util.copy(items, true));
            }
            var oldValue = select.val() || '';
            select.html('<option value="">'+(title || '')+'</option>');
            for(var i in items){
                $('<option value="'+items[i][item_key || 0]+'">'+items[i][item_name || 1]+'</option>').appendTo(select);
            }
            select.selectpicker('refresh');
            select.val(oldValue).change();
        };
        if(items instanceof App.Model){
            this.models.push({
                model: items,
                taskName: items.addTask(function(items){
                    render(items);
                })
            });
        }else{
            render(items);
        }
        return selectContainer;
    },
    flushData: function(){
        var data = {};
        this.frame.find('.filter-form .filter-item input').each(function(){
            var name = $(this).attr('name');
            var value = $(this).val();
            data[name] = value;
        });
        this.frame.find('.filter-form .filter-item select').each(function(){
            var name = $(this).attr('name');
            var value = $(this).val();
            data[name] = value;
        });
        this.data = data;
        return this;
    },
    getData: function(){
        return this.flushData().data;
    },
    reset: function(){
        this.frame.find('.filter-form .filter-item input').val('').change();
        this.frame.find('.filter-form .filter-item select').val('').change();
    },
    destroy: function(){
        for(var i in this.models){
            this.models[i].model.removeTask(this.models[i].taskName);
        }
        this.frame.remove();
    }
};
App.DataTable.Floatable = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.table = this.dataTable.table.frame;
    this.frame = null;
    this.header = null;
    this.footer = null;
    this.navHeight = 45;
    this.needFlush = true;
    this.sleeped = true;
    this.scrolled = false;
    this.init();
};
App.DataTable.Floatable.prototype = {
    constructor: App.DataTable.Floatable,
    init: function(){
        var self = this;
        this.frame = $([
            '<div class="filter clearfix pull-left">',
            '    <form class="filter-form form-inline">',
            '        <button type="submit" class="refresh-btn btn btn-default btn-sm no-margin">',
            '            <i class="fa fa-search"></i>',
            '        </button>',
            '    </form>',
            '</div>'
        ].join(''));
        this.header = this.getHeader();
        this.footer = this.getFooter();
        this.scroll();
        $('#sizeToggle').click(function(){
            self.resize();
        });
        $('#menuToggle').click(function(){
            self.resize();
        });
        $(window).resize(function(){
            self.resize();
        });
        this.dataTable.after.add('floatable_flush', function(){
            self.flushHeader().flushFooter();
        });
    },
    floatAll: function(){
        var scrollTop = $(document).scrollTop();
        //悬浮表头
        if(this.header){
            if(scrollTop+this.navHeight > this.table.offset().top){
                this.header.show();
            }else{
                this.header.hide();
            }
        }
        //悬浮分页
        if(this.footer){
            var footerBottom = this.table.offset().top+this.table.outerHeight()+this.footer.outerHeight();
            var windowHeight = $(window).height();
            if(windowHeight+scrollTop < footerBottom){
                this.footer.addClass('floated');
            }else{
                this.footer.removeClass('floated');
            }
        }
    },
    getHeader: function(){
        if(!this.table){
            this.header = null;
            return this;
        }
        var header = this.table.find('thead tr');
        var headerFloat = header.clone(true).hide().addClass('header-floated');
        header.after(headerFloat);
        return headerFloat;
    },
    flushHeader: function(){
        if(!this.header){
            return this;
        }
        var header = this.table.find('thead tr');
        var headerFloat = this.header;
        this.header.css('width', this.table.outerWidth());
        var i = 0;
        header.children().each(function(){
            var width = $(this).attr('width') || $(this).outerWidth(true);
            var j = 0;
            headerFloat.children().each(function(){
                if(i === j && width){
                    $(this).attr('width', width);
                }
                j++;
            });
            i++;
        });
        return this;
    },
    getFooter: function(){
        return this.dataTable.paginator ? this.dataTable.paginator.frame : null;
    },
    flushFooter: function(){
        if(this.footer){
            this.footer.css('width', this.table.outerWidth());
        }
        return this;
    },
    sleep: function(){
        this.unscroll();
        this.sleeped = true;
    },
    wakeup: function(){
        if(!this.sleeped){
            return ;
        }
        if(this.needFlush){
            this.flushHeader().flushFooter();
            this.needFlush = false;
        }
        this.scroll();
        this.sleeped = false;
    },
    resize: function(){
        if(this.sleeped){
            this.needFlush = true;
        }else{
            this.flushHeader().flushFooter();
        }
    },
    scroll: function(){
        var self = this;
        if(!this.scrolled){
            $(document).on('scroll.data-table-'+this.dataTable.id, function(){
                self.floatAll();
            });
            this.scrolled = true;
        }
        return this;
    },
    unscroll: function(){
        if(this.scrolled){
            $(document).off('scroll.data-table-'+this.dataTable.id);
            this.scrolled = false;
        }
    },
    destroy: function(){
        this.unscroll();
        this.frame.remove();
    }
};
App.DataTable.Menu = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.table = null;
    this.activeClass = 'success';
    this.hoverClass = 'hover';
    this.isHeadInitd = false;
    this.menuMaker = null;
    this.globleMinWidth = 0;
    this.init();
};
App.DataTable.Menu.prototype = {
    constructor: App.DataTable.Menu,
    init: function(){
        this.table = this.dataTable.table.frame;
        this.menuMaker = new App.DataTable.Menu.Maker(this.dataTable, this.config);
        this.initGlobleMenu();
    },
    flush: function(){
        this.initInlineMenu();
        this.initPopMenu();
    },
    getGlobleMinWidth: function(){
        return this.globleMinWidth;
    },
    initGlobleMenu: function(){
        var self = this;
        var menu = self.menuMaker.getGlobleMenu();
        this.globleMinWidth = App.util.size(menu).width;
        menu.appendTo(self.dataTable.frame.find('.header'));
    },
    initInlineMenu: function(){
        var self = this;
        //创建菜单
        self.table.find('tbody tr').each(function(){
            var data = JSON.parse($(this).attr('data-data'));
            var inlineMenu = self.menuMaker.getInlileMenu(data);
            if(inlineMenu){
                inlineMenu.appendTo($(this).find('> td.column-main'));
            }
        });
        //悬浮效果
        self.table.find('tbody tr').hover(function(){
            $(this).addClass(self.hoverClass);
            if(self.isActive()){
                return ;
            }
            $(this).addClass(self.hoverClass).find('.menu-inline').show();
        },function(){
            $(this).removeClass(self.hoverClass);
            if(self.isActive()){
                return ;
            }
            $(this).find('.menu-inline').removeClass('open').hide();
        });
        //更多
        self.table.find('.more-btn').click(function(){
            if(self.isActive() && !$(this).parent().hasClass('open')){
                return ;
            }
            var menu = $(this).parent();
            if(!menu.hasClass('open')){
                //高度适应
                var bottom = $(window).height() - (menu.offset().top + menu.outerHeight() - $(window).scrollTop());
                var menuHeight = menu.find('> ul').outerHeight();
                if(menuHeight > bottom){
                    menu.addClass('up');
                }
                //左右适应
                var left = $(window).width() - (menu.offset().left + menu.outerWidth());
                var menuWidth = menu.find('> ul').outerWidth();
                if(menuWidth > left){
                    menu.addClass('left');
                }
                menu.addClass('open');
                menu.parent().parent().addClass(self.activeClass);
                //关闭菜单
                $(document).one('click', function(e){
                    if(e.button === 2){//火狐右键点击
                        $(document).one('click', function(e){
                            menu.removeClass('open');
                            menu.removeClass('up');
                            menu.removeClass('left');
                            self.cancelActive();
                            return true;
                        });
                    }else{
                        menu.removeClass('open');
                        menu.removeClass('up');
                        menu.removeClass('left');
                        self.cancelActive();
                        return false;
                    }
                });
            }else{
                menu.removeClass('open');
                menu.removeClass('up');
                menu.removeClass('left');
                self.cancelActive();
            }
            return false;
        });
    },
    initPopMenu: function(){
        var self = this;
        var selector = this.isHeadInitd ? 'tbody > tr' : 'tr';
        //右键点击
        self.table.find(selector).mouseup(function(e){
            if(3 === e.which){
                var dataRaw = $(this).attr('data-data');
                var data = dataRaw ? JSON.parse(dataRaw) : false;
                //取消以前的激活状态
                self.cancelActive();
                //设置当前行为激活状态
                if(data){
                    $(this).addClass(self.activeClass).addClass(self.hoverClass).find('.menu-inline').show();
                }
                //获取菜单并显示
                var menu = self.menuMaker.getPopMenu(data);
                menu.css('left', $(window).width());
                menu.appendTo(self.dataTable.frame);
                menu.show(10, function(){
                    var getTop = function(offset){
                        return e.pageY - self.table.parent().offset().top - (offset || 0) - 5;
                    };
                    var getLeft = function(offset){
                        return e.pageX - self.table.parent().offset().left - (offset || 0);
                    };
                    var menuWidth = menu.find('> ul').outerWidth();
                    var menuHeight = menu.find('> ul').outerHeight();
                    var bottom = $(window).height() - (e.pageY - $(window).scrollTop());
                    var left = $(window).width() - e.pageX;
                    var menuLeft = left < menuWidth ? getLeft(menuWidth - left) : getLeft();
                    var menuTop = bottom < menuHeight ? getTop(menuHeight - bottom) : getTop();
                    menu.css('left', menuLeft);
                    menu.css('top', menuTop);
                    if(left < menuWidth*2){
                        menu.addClass('left');
                    }
                });
                //隐藏菜单
                $(document).one('click', function(e){
                    if(e.button === 2){//火狐右键点击
                        $(document).one('click', function(e){
                            menu.hide().remove();
                            self.cancelActive();
                            menu = null;
                            return true;
                        });
                    }else{
                        menu.hide().remove();
                        self.cancelActive();
                        menu = null;
                    }
                    return true;
                });
                return false;
            }
        });
        this.isHeadInitd = true;
    },
    isActive: function(){
        return this.table.find('tr.'+this.activeClass).length > 0;
    },
    cancelActive: function(){
        var self = this;
        self.table.find('tr.'+self.activeClass).each(function(){
            if(!$(this).hasClass(self.hoverClass)){
                $(this).find('.menu-inline').removeClass('open').hide();
            }
            $(this).removeClass(self.activeClass);
        });
        self.table.find('tr.'+self.hoverClass+' .menu-inline').removeClass('open').show();
    },
    destroy: function(){
        
    }
};
App.DataTable.Menu.Maker = function(dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.activeClass = 'success';
    this.hoverClass = 'hover';
    this.isHeadInitd = false;
    this.init();
};
App.DataTable.Menu.Maker.prototype = {
    constructor: App.DataTable.Menu.Maker,
    init: function(){
        //配置合并
        for(var i in this.config){
            var item = this.config[i];
            if(item.parent && this.generalMenus[item.parent]){
                var parent = this.generalMenus[item.parent];
                for(var key in parent){
                    if(item[key] === undefined){
                        item[key] = parent[key];
                    }
                }
            }
        }
        //权限校验
        var myMenus = [];
        for(var i in this.config){
            var item = this.config[i];
            if(App.permission.check(item['permission'])){
                myMenus.push(item);
                if(!item['items']){
                    continue;
                }
                var items = [];
                for(var i in item['items']){
                    if(App.permission.check(item['items'][i]['permission'])){
                        items.push(item['items'][i]);
                    }
                }
                item['items'] = items;
            }
        }
        this.config = myMenus;
    },
    getGlobleMenu: function(){
        var container = $([
            '<div class="menus pull-right clearfix">',
            '     <ul class="btn-group no-margin pull-right">',
            '     </ul>',
            '</div>'
        ].join(''));
        var ul = container.find('> ul');
        //全局操作
        var config = this.config;
        for(var i in config){
            if(config[i].type === 'globe'){
                var item = this.getBtnItem(config[i]);
                item.appendTo(ul);
                this.bindGlobeOperation(item, config[i]);
            }
        }
        //批量操作
        var items = this.getMultiItems();
        if(items.length > 0 && this.dataTable.table.config.withSelect){
            var multiContainer = $([
                '<li class="btn-group">',
                '    <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">',
                '        批量操作 <span class="caret"></span>',
                '    </button>',
                '    <ul class="dropdown-menu multi-level">',
                '    </ul>',
                '</li>'
            ].join(''));
            var multiUl = multiContainer.find('> ul');
            for(var i in items){
                items[i].appendTo(multiUl);
            }
            multiContainer.appendTo(ul);
        }
        //公共操作
        /*this.getBtnItem({type: 'globe', icon: '<i class="fa fa-download"></i>'}).appendTo(ul);
        this.getBtnItem({type: 'globe', icon: '<i class="fa fa-cog"></i>'}).appendTo(ul);*/
        return container;
    },
    getPopMenu: function(data){
        var container = $([
            '<div class="menu-pop open">',
            '    <ul class="dropdown-menu multi-level">',
            '    </ul>',
            '</div>'
        ].join(''));
        var ul = container.find('> ul');
        //导航按钮组
        //this.getNav().appendTo(ul);
        //this.getSeparator().appendTo(ul);
        //全局操作
        var items = this.getGlobeItems();
        if(items.length > 0){
            for(var i in items){
                items[i].appendTo(ul);
            }
            this.getSeparator().appendTo(ul);
        }
        //行内操作
        if(data){
            var items = this.getInlineItems(data);
            if(items.length > 0){
                for(var i in items){
                    items[i].appendTo(ul);
                }
                this.getSeparator().appendTo(ul);
            }
        }
        //批量操作
        items = this.getMultiItems();
        if(items.length > 0 && this.dataTable.table.config.withSelect){
            var multiContainer = this.getOpenable({name:'批量操作'});
            var multiUl = multiContainer.find('> ul');
            for(var i in items){
                items[i].appendTo(multiUl);
            }
            multiContainer.appendTo(ul);
            this.getSeparator().appendTo(ul);
        }
        //公共操作
        /*this.getItem({type: 'globe', name: '导出数据'}).appendTo(ul);
        this.getItem({type: 'globe', name: '列表设置'}).appendTo(ul);*/
        return container;
    },
    getInlileMenu: function(data){
        var floatItems = this.getFloatItems(data);
        var inlineItems = this.getInlineItems(data);
        if(inlineItems.length < 1){
            return null;
        }
        var container = $([
            '<div class="menu-inline">',
            '    <a class="more-btn" href="javascript:void(0);">',
            '        <i class="fa fa-ellipsis-h"></i>',
            '    </a>',
            '    <ul class="dropdown-menu dropdown-menu-right multi-level">',
            '    </ul>',
            '</div>'
        ].join(''));
        var ul = container.find('> ul');
        var moreBtn = container.find('> .more-btn');
        //悬浮菜单
        var floatItems = this.getFloatItems(data);
        for(var i in floatItems){
            moreBtn.before(floatItems[i]);
        }
        if(floatItems.length >= inlineItems.length){
            moreBtn.remove();
            return container;
        }
        //行内菜单
        for(var i in inlineItems){
            ul.append(inlineItems[i]);
        }
        return container;
    },
    getFloatItems: function(data){
        var config = this.config;
        var items = [];
        for(var i in config){
            if(config[i].type === 'inline' && config[i].floatable ){
                if(this.dataTable.condition.match(data, config[i].conditions)){
                    var item = this.getFloatItem(config[i], data);
                    this.bindInlineOperation(item, config[i], data);
                    items.push(item);
                }
            }
        }
        return items;
    },
    getGlobeItems: function(){
        var config = this.config;
        var items = [];
        for(var i in config){
            if(config[i].type === 'globe'){
                items.push(this.getItem(config[i]));
            }
        }
        return items;
    },
    getInlineItems: function(data){
        var config = this.config;
        var items = [];
        for(var i in config){
            if(config[i].type === 'inline'){
                var item = this.getItem(config[i], data);
                if(item){
                    items.push(item);
                }
            }
        }
        return items;
    },
    getMultiItems: function(){
        var config = this.config;
        var items = [];
        for(var i in config){
            if(config[i].type === 'multi'){
                var item = this.getItem(config[i]);
                if(item){
                    items.push(item);
                }
            }
        }
        return items;
    },
    getNav: function(){
        var nav = $([
            '<li class="menu-nav row">',
            '    <div class="col-lg-4 text-center no-padding" >',
            '        <a href="javascript:void(0);" title="返回">',
            '            <span class="menu-icon">',
            '                <i class="fa fa-arrow-left"></i> ',
            '            </span>',
            '        </a>',
            '    </div>',
            '    <div class="col-lg-4 text-center no-padding" >',
            '        <a href="javascript:void(0);" title="刷新">',
            '            <span class="menu-icon">',
            '                <i class="fa fa-refresh"></i>',
            '            </span>',
            '        </a>',
            '    </div>',
            '    <div class="col-lg-4 text-center no-padding" >',
            '        <a href="javascript:void(0);" title="前进">',
            '            <span class="menu-icon">',
            '                <i class="fa fa-arrow-right"></i> ',
            '            </span>',
            '        </a>',
            '    </div>',
            '</li>'
        ].join(''));
        return nav;
    },
    getFloatItem: function(config){
        var menu = $([
            '<a class="float-btn" href="javascript:void(0);" title="'+config.name+'">',
            '    '+(config.icon ? config.icon : config.name),
            '</a>'
        ].join(''));
        return menu;
    },
    getBtnItem: function(config){
        if(!config.openable){
            return $([
                '<li class="btn btn-default btn-sm">',
                '   '+(config.icon || '')+(config.name ? ' '+config.name : ''),
                '</li>'
            ].join(''));
        }
        var multiContainer = $([
            '<li class="btn-group">',
            '    <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">',
            '        '+(config.icon || '')+(config.name ? ' '+config.name : '')+' <span class="caret"></span>',
            '    </button>',
            '    <ul class="dropdown-menu multi-level">',
            '    </ul>',
            '</li>'
        ].join(''));
        for(var i in config.items){
            multiContainer.find('> ul').append(this.getItem(config.items[i]));
        }
        return multiContainer;
    },
    getItem: function(config, data){
        if(!config.openable){
            var item = $('<li><a href="javascript:void(0);">'+config.name+'</a></li>');
            if(config.type === 'inline'){
                if(this.dataTable.condition.match(data, config.conditions)){
                    this.bindInlineOperation(item, config, data);
                    return item;
                }else{
                    return false;
                }
            }else if(config.type === 'multi'){
                this.bindMultiOperation(item, config);
                return item;
            }else if(config.type === 'globe'){
                this.bindGlobeOperation(item, config);
                return item;
            }
            return false;
        }
        var sub = this.getOpenable(config);
        var ul = sub.find('> ul');
        for(var i in config['items']){
            var subConfig = config['items'][i];
            if(!subConfig.openable){
                var item = this.getItem(subConfig, data);
                if(item){
                    ul.append(item);
                }
                continue;
            }
            var sub1 = this.getOpenable(subConfig);
            var ul1 = sub1.find('> ul');
            for(var j in subConfig['items']){
                var subConfig1 = subConfig['items'][j];
                if(!subConfig1.openable){
                    var item1 = this.getItem(subConfig1, data);
                    if(item1){
                        ul1.append(item1);
                    }
                    continue;
                }
                var sub2 = this.getOpenable(subConfig1);
                var ul2 = sub2.find('> ul');
                for(var j in subConfig1['items']){
                    var item2 = this.getItem(subConfig1['items'][j], data);
                    if(item2){
                        ul2.append(item2);
                    }
                }
                ul1.append(sub2);
            }
            ul.append(sub1);
        }
        return sub;
    },
    getOpenable: function(config){
        var container = $([
            '<li class="openable">',
            '    <a href="javascript:void(0);">',
            '        '+config.name,
            '        <span class="more">',
            '            <i class="fa fa-chevron-right"></i>',
            '        </span>',
            '    </a>',
            '    <ul class="dropdown-menu">',
            '    </ul>',
            '</li>'
        ].join(''));
        return container;
    },
    getSeparator: function(){
        return $('<li role="separator" class="divider"></li>');
    },
    bindGlobeOperation: function(item, config){
        var self = this;
        var operation = config.operation;
        //自定义函数
        if(typeof operation === 'function'){
            item.click(function(){
                operation(self.dataTable);
            });
        }
    },
    bindInlineOperation: function(item, config, data){
        var self = this;
        var operation = config.operation;
        if(typeof operation === 'function'){//自定义操作
            item.click(function(){
                operation(self.dataTable, data);
            });
        }else if(operation.type === 'change'){//单修改
            var primaryKeyName = self.dataTable.config.table.id;
            var primaryKey = data[primaryKeyName];
            var postData = {};
            postData[primaryKeyName] = primaryKey;
            var expectDatas = operation.data;
            if(expectDatas){
                for(var i in expectDatas){
                    var key = expectDatas[i];
                    postData[key] = data[key];
                }
            }
            item.click(function(){
                self.dataTable.postData({
                    url: operation.url,
                    data: postData,
                    alert: '操作成功！',
                    reload: true
                });
            });
        }
    },
    bindMultiOperation: function(item, config){
        var self = this;
        var operation = config.operation;
        //自定义函数
        if(typeof operation === 'function'){
            item.click(function(){
                operation(self.dataTable);
            });
        }
        //修改
        if(operation.type === 'change'){
            item.click(function(){
                var primaryKeysName = self.dataTable.config.table.id+'s';
                var ids = self.dataTable.table.getSelectedIds();
                //判断选中条目
                if(ids.length < 1){
                    App.util.hint.alert({
                        text: '没有选择数据！'
                    });
                    return ;
                }
                //确认
                App.util.hint.confirm({
                    text: '确定要'+config.name+'选中的'+ids.length+'项吗？',
                    yes: function(){
                        var data = {};
                        data[primaryKeysName] = ids.join(',');
                        self.dataTable.postData({
                            url: operation.url,
                            data: data,
                            alert: config.name+'成功！',
                            reload: true
                        });
                    }
                });
            });
        }
    },
    generalMenus: {
        add: {
            type: 'globe',
            name: '添加',
            icon: '<i class="fa fa-plus"></i>',
            floatable: false,
            operation: function(dataTable){
                dataTable.detail.add();
            }
        },
        view: {
            type: 'inline',
            name: '查看',
            icon: '<i class="fa fa-eye"></i>',
            floatable: true,
            operation: function(dataTable, data){
                dataTable.detail.view(data);
            }
        },
        edit: {
            type: 'inline',
            name: '编辑',
            icon: '<i class="fa fa-edit"></i>',
            floatable: true,
            operation: function(dataTable, data){
                dataTable.detail.edit(data);
            }
        },
        drop: {
            type: 'inline',
            name: '删除',
            icon: '<i class="fa fa-trash-o"></i>',
            floatable: true,
            operation: function(dataTable, data){
                App.util.hint.confirm({
                    text: '确定要删除吗？',
                    yes: function(){
                        var id = dataTable.config.table.id;
                        var postData = {};
                        postData[id] = data[id];
                        dataTable.postData({
                            url: 'delete',
                            data: postData,
                            alert: '删除成功！',
                            reload: true
                        });
                    }
                });
            }
        },
        multiDrop: {
            type: 'multi',
            name: '删除',
            operation: function(dataTable){
                var primaryKeysName = dataTable.config.table.id+'s';
                var ids = dataTable.table.getSelectedIds();
                //判断选中条目
                if(ids.length < 1){
                    App.util.hint.alert({
                        text: '没有选择数据！'
                    });
                    return ;
                }
                //确认
                
                App.util.hint.confirm({
                    text: '确定要删除选中的'+ids.length+'项数据吗？',
                    yes: function(){
                        var data = {};
                        data[primaryKeysName] = ids.join(',');
                        dataTable.postData({
                            url: 'multiDelete',
                            data: data,
                            alert: '删除成功！',
                            reload: true
                        });
                    }
                });
            }
        },
        oplog: {
            type: 'inline',
            name: '操作日志',
            floatable: false,
            operation: function(dataTable, data){
                var idName = dataTable.config.table.id;
                var id = data[idName];
                dataTable.oplog.win.show();
                dataTable.oplog.loadData(id);
            }
        }
    }
};
App.DataTable.Oplog = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.win = null;
    this.logDataTable = null;
    this.init();
};
App.DataTable.Oplog.prototype = {
    constructor: App.DataTable.Oplog,
    init: function(){
        var self = this;
        //弹窗
        var winConfig = this.config.win || {};
        winConfig.name = this.dataTable.id+'-oplog';
        winConfig.width = '450px';
        this.win = new App.Win(winConfig);
        this.win.hide();
        this.win.setTitle('操作日志');
        //列表
        this.logDataTable = new App.DataTable({
            baseUrl: self.dataTable.config.baseUrl,
            table: {
                id: 'id',
                main: 0,
                showId: false,
                withSelect: false,
                columns: [
                    {name: '操作名称', value: 'op_name'},
                    {name: '操作用户', width: '150px', value: 'nickname'},
                    {name: '操作时间', width: '140px', value: function(item){
                        return App.util.formator.dateTime(item.time);
                    }}
                ]
            },
            floatable: false,
            paginator: false,
            oplog: false
        });
        this.logDataTable.table.frame.removeClass('table-hover').removeClass('table-striped');
        this.logDataTable.fix(this.win.contentContainer);
        this.dataTable.wins.add(this.win, 'oplog');
    },
    loadData: function(id){
        this.logDataTable.config.dataUrl = 'getOpLogs?id='+id;
        this.logDataTable.loadData();
    },
    destroy: function(){
        this.logDataTable.destroy();
    }
};
App.DataTable.Paginator = function (dataTable, config){
    this.config = config;
    this.dataTable = dataTable;
    this.table = this.dataTable.table.frame;
    this.frame = null;
    this.pageSize = 25;
    this.currentPage = 1;
    this.totalPages = 1;
    this.totalItems = 0;
    this.init();
};
App.DataTable.Paginator.prototype = {
    constructor: App.DataTable.Paginator,
    init: function(pagesize){
        var self = this;
        this.frame = this.getFrame();
        this.pageSize = pagesize || 25;
        this.initPageSizeChange();
        this.table.after(this.frame);
        //全选
        if(this.dataTable.table.config.withSelect){
            self.frame.find('.chk-all').click(function(){
                var checked = $(this).prop('checked');
                self.table.find('.chk-row').not(':disabled').each(function(){
                    $(this).prop('checked', checked).change();
                });
            });
        }else{
            self.frame.find('.chk-all-btn-container').remove();
        }
        //隐藏
        this.frame.hide();
        //移动端处理
        if(App.size === 'sm'){
            this.frame.find('.li-total-items-num').remove();
            this.frame.find('.li-total-pages-num').remove();
            
            this.frame.find('.first-page-btn').remove();
            //this.frame.find('.prev-page-btn').remove();
            //this.frame.find('.next-page-btn').remove();
            this.frame.find('.last-page-btn').remove();
        }
    },
    getFrame: function(){
        return $([
            '<div class="footer clearfix">',
            '    <ul class="paginate-stats btn-group pagination-sm no-margin pull-left">',
            '        <li class="btn btn-default btn-sm chk-all-btn-container">',
            '            <label class="no-margin">',
            '                <input type="checkbox" class="chk-all no-margin no-padding"> 全选',
            '            </label>',
            '        </li>',
            '        <li class="btn btn-default btn-sm li-total-items-num">',
            '            共<span class="total-items-num">0</span>条记录',
            '        </li>',
            '        <li class="btn-group dropup">',
            '            <button class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">',
            '                每页<span class="page-size-num"></span>条 <span class="caret"></span>',
            '            </button>',
            '            <ul class="dropdown-menu">',
            '                <li class="page-size-btn" data-value="10"><a href="javascript:void(0);">10</a></li>',
            '                <li class="page-size-btn" data-value="25"><a href="javascript:void(0);">25</a></li>',
            '                <li class="page-size-btn" data-value="50"><a href="javascript:void(0);">50</a></li>',
            '                <li class="page-size-btn" data-value="75"><a href="javascript:void(0);">75</a></li>',
            '                <li class="page-size-btn" data-value="100"><a href="javascript:void(0);">100</a></li>',
            '                <li class="page-size-btn" data-value="200"><a href="javascript:void(0);">200</a></li>',
            '                <li class="page-size-btn" data-value="500"><a href="javascript:void(0);">500</a></li>',
            '            </ul>',
            '        </li>',
            '        <li class="btn btn-default btn-sm li-total-pages-num">',
            '            共<span class="total-pages-num">0</span>页',
            '        </li>',
            '    </ul>',
            '    <ul class="paginate-pages pagination pagination-sm no-margin pull-right">',
            '        <li class="first-page-btn disabled">',
            '            <a href="javascript:void(0);" title="首页">',
            '                <i class="fa fa-fast-backward"></i>',
            '            </a>',
            '        </li>',
            '        <li class="prev-page-btn disabled">',
            '            <a href="javascript:void(0);" title="上一页">',
            '                <i class="fa fa-backward"></i>',
            '            </a>',
            '        </li>',
            '        <li class="next-page-btn disabled">',
            '            <a href="javascript:void(0);" title="下一页">',
            '                <i class="fa fa-forward"></i>',
            '            </a>',
            '        </li>',
            '        <li class="last-page-btn disabled">',
            '            <a href="javascript:void(0);" title="末页">',
            '                <i class="fa fa-fast-forward "></i>',
            '            </a>',
            '        </li>',
            '    </ul>',
            '</div>'
        ].join(''));
    },
    page: function(page){
        this.currentPage = page;
        this.dataTable.loadData();
    },
    initPageSizeChange: function(){
        var self = this;
        this.frame.find('.page-size-btn').click(function(){
            if($(this).hasClass('active')){
                return ;
            }
            $(this).siblings().removeClass('active');
            $(this).addClass('active');
            var size = $(this).attr('data-value');
            $(this).parent().parent().find('.page-size-num').text(size);
            self.pageSize = size;
            self.page(1);
        });
        this.frame.find('.page-size-num').text(self.pageSize);
        this.frame.find('.page-size-btn').each(function(){
            if($(this).attr('data-value') === self.pageSize+''){
                $(this).addClass('active');
            }else{
                $(this).removeClass('active');
            }
        });
    },
    flush: function(total){
        this.totalItems = total || 0;
        this.totalPages = Math.ceil(total/this.pageSize);
        this.flushSats();
        this.flushBtns();
        if(App.size === 'sm'){
            this.flushMinNumBtns();
        }else{
            this.flushNumBtns();
        }
        if(this.totalItems <= this.pageSize){
            this.frame.hide();
        }else{
            this.frame.show();
        }
        return this;
    },
    flushMinNumBtns: function(){
        this.frame.find('.num-btn').remove();
        var totalPages = this.totalPages;
        var currentPage = this.currentPage;
        if(totalPages < 8){//不会出现省略
            for(var i=1;i<=totalPages;i++){
                this.appendBtn(this.getNumBtn(i, currentPage === i));
            }
        }else{
            if(currentPage < 5){//前
                for(var i=1;i<=5;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
                this.appendBtn(this.getEmptyBtn());
                this.appendBtn(this.getNumBtn(totalPages, false));
            }else if(totalPages-currentPage < 4){//后
                this.appendBtn(this.getNumBtn(1, false));
                this.appendBtn(this.getEmptyBtn());
                for(var i=totalPages-4;i<=totalPages;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
            }else{//中间
                this.appendBtn(this.getNumBtn(1, false));
                this.appendBtn(this.getEmptyBtn());
                for(var i=currentPage-1;i<=currentPage+1;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
                this.appendBtn(this.getEmptyBtn());
                this.appendBtn(this.getNumBtn(totalPages, false));
            }
        }
    },
    flushNumBtns: function(){
        this.frame.find('.num-btn').remove();
        var totalPages = this.totalPages;
        var currentPage = this.currentPage;
        if(totalPages < 12){//不会出现省略
            for(var i=1;i<=totalPages;i++){
                this.appendBtn(this.getNumBtn(i, currentPage === i));
            }
        }else{
            if(currentPage < 7){//前
                for(var i=1;i<=8;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
                this.appendBtn(this.getEmptyBtn());
                for(var i=totalPages-1;i<=totalPages;i++){
                    this.appendBtn(this.getNumBtn(i, false));
                }
            }else if(totalPages-currentPage < 6){//后
                for(var i=1;i<=2;i++){
                    this.appendBtn(this.getNumBtn(i, false));
                }
                this.appendBtn(this.getEmptyBtn());
                for(var i=totalPages-7;i<=totalPages;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
            }else{//中间
                for(var i=1;i<=2;i++){
                    this.appendBtn(this.getNumBtn(i, false));
                }
                this.appendBtn(this.getEmptyBtn());
                for(var i=currentPage-3;i<=currentPage+3;i++){
                    this.appendBtn(this.getNumBtn(i, i === currentPage));
                }
                this.appendBtn(this.getEmptyBtn());
                for(var i=totalPages-1;i<=totalPages;i++){
                    this.appendBtn(this.getNumBtn(i, false));
                }
            }
        }
    },
    flushSats: function(){
        this.frame.find('.total-items-num').text(this.totalItems);
        this.frame.find('.total-pages-num').text(this.totalPages);
    },
    flushBtns: function(){
        var self = this;
        //首页
        var firstPageBtn = this.frame.find('.first-page-btn');
        firstPageBtn.unbind('click').addClass('disabled');
        if(this.currentPage > 1){
            firstPageBtn.removeClass('disabled').click(function(){
                self.page(1);
            });
        }
        //上一页
        var prevPageBtn = this.frame.find('.prev-page-btn');
        prevPageBtn.unbind('click').addClass('disabled');
        if(this.currentPage > 1){
            prevPageBtn.removeClass('disabled').click(function(){
                self.page(self.currentPage-1);
            });
        }
        //下一页
        var nextPageBtn = this.frame.find('.next-page-btn');
        nextPageBtn.unbind('click').addClass('disabled');
        if(this.totalPages - this.currentPage > 0){
            nextPageBtn.removeClass('disabled').click(function(){
                self.page(self.currentPage+1);
            });
        }
        //末页
        var lastPageBtn = this.frame.find('.last-page-btn');
        lastPageBtn.unbind('click').addClass('disabled');
        if(this.totalPages - this.currentPage > 0){
            lastPageBtn.removeClass('disabled').click(function(){
                self.page(self.totalPages);
            });
        }
    },
    getNumBtn: function(page, active){
        var self = this;
        if(active){
            var btn = $('<li class="num-btn"><span>'+page+'</span></li>');
            btn.addClass('active');
        }else{
            var btn = $('<li class="num-btn"><a href="javascript:void(0);">'+page+'</a></li>');
            btn.click(function(){
                self.page(page);
            });
        }
        return btn;
    },
    getEmptyBtn: function(){
        return $([
            '<li class="num-btn disabled">',
            '    <span>...</span>',
            '</li>'
        ].join(''));
    },
    appendBtn: function(btn){
        this.frame.find('.next-page-btn').before(btn);
    },
    destroy: function(){
        this.frame.remove();
    }
};
//页面容器
App.pages = {
    pages: [],
    get: function(name){
        var pages = this.pages;
        for(var i in pages){
            if(pages[i].name === name){
                return pages[i];
            }
        }
        return false;
    },
    add: function(page){
        this.pages.push(page);
    }
};
//页面容器
App.models = {
    models: [],
    get: function(name){
        var models = this.models;
        for(var i in models){
            if(models[i].name === name){
                return models[i];
            }
        }
        return false;
    },
    add: function(model){
        this.models.push(model);
    }
};
//导航
App.nav = {
    maxRecord: 10,
    current: null,
    pages: [],
    init: function(){
        var self = this;
        $('.nav-back-btn').click(function(){
            self.back();
        });
        $('.nav-refresh-btn').click(function(){
            self.refresh();
        });
        $('.nav-forward-btn').click(function(){
            self.forward();
        });
    },
    open: function(name, params){//打开页面
        var pages = this.pages;
        var page = null;
        //获取页面
        if(this.index(name) > -1){
            page = this.find(name);
            //历史记录删除
            var index = this.index(name);
            if(index > -1){
                pages.splice(index, 1);
            }
        }else{
            //获取新页面
            page = App.pages.get(name);
            page.init();
        }
        //设置参数
        page.setParams(params);
        //添加历史记录
        pages.push(page);
        //缓存长度控制
        if(pages.length > this.maxRecord){
            var deleteds = pages.splice(0, pages.length - this.maxRecord);
            for(var i in deleteds){
                //销毁页面
                deleteds[i].destroy();
            }
        }
        //隐藏当前页面
        var currentPage = this.current;
        if(currentPage){
            currentPage.hide();
        }
        //替换当前页面
        this.current = page;
        //显示新页面
        page.show();
        //改变菜单位置
        App.menu.change(page.name);
        //改变历史记录列表
        App.history.flush(this.pages);
    },
    refresh: function(){
        if(this.current){
            this.current.refresh();
        }
    },
    back: function(){//后退
        if(!this.current){
            return ;
        }
        var index = this.index(this.current);
        if(index < 1){
            return ;
        }
        this.current.hide();
        var prePage = this.pages[index-1];
        prePage.show();
        this.current = prePage;
        //改变菜单位置
        App.menu.change(prePage.name);
    },
    forward: function(){//前进
        if(!this.current){
            return ;
        }
        var index = this.index(this.current);
        if(index < 0 || this.pages.length - index < 2){
            return ;
        }
        this.current.hide();
        var nextPage = this.pages[index+1];
        nextPage.show();
        this.current = nextPage;
        //改变菜单位置
        App.menu.change(nextPage.name);
    },
    find: function(name){//从页面池中查找页面
        for(var i in this.pages){
            if(this.pages[i].name === name){
                return this.pages[i];
            }
        }
        return false;
    },
    index: function(page){//获取活动页面的索引值
        var pages = this.pages;
        if(typeof page === 'object'){
            return pages.indexOf(page);
        }
        for(var i in pages){
            if(pages[i].name === page){
                return parseInt(i);
            }
        }
        return -1;
    }
};
//历史记录
App.history = {
    flush: function(pageStack){
        var pages = [];
        for(var i in pageStack){
            pages.unshift(pageStack[i]);
        }
        var ul = $('.dropdown-menu.history');
        ul.children().remove();
        $('<li><a href="javascript:void(0);">共 '+pages.length+' 条历史记录</a></li>').appendTo(ul);
        for(var i in pages){
            var page = pages[i];
            var item = $([
                '<li>',
                '    <a href="javascript:void(0);" style="padding-top: 3px;padding-bottom: 3px;">',
                '        <ul class="breadcrumb no-padding" style="border: none;">',
                '        </ul>',
                '    </a>',
                '</li>'
            ].join(''));
            var itemUl = item.find('ul');
            for(var i in page.breadcrumb){
                var breadItem = page.breadcrumb[i];
                var li = $('<li>'+breadItem.content+'</li>');
                if(breadItem.icon){
                    li.prepend($('<span style="display:inline-block;width: 5px;"></span>'));
                    var icon = typeof breadItem.content === 'string' ? $(breadItem.icon) : breadItem.icon;
                    icon.addClass('fa-fw');
                    li.prepend(icon);
                }
                li.appendTo(itemUl);
            }
            item.click((function(){
                var page_ = page;
                return function(){
                    App.nav.open(page_.name, page_.params);
                };
            })());
            item.appendTo(ul);
        }
    }
};
//权限
App.permission = {
    callback: null,
    operations: [],
    groups: [],
    init: function(success){
        this.callback = success;
        this.load();
    },
    load: function(){
        var self = this;
        self.isLoading = true;
        App.util.request.getData({
            url: 'permission',
            loading: new App.Loading({}),
            success: function(data){
                self.operations = data.operations;
                self.groups = data.groups;
                self.callback();
            },
            error: function(){
                setTimeout(function(){
                    self.load();
                }, 1000);
            }
        });
    },
    check: function(operation){
        if(App.debug){
            return true;
        }
        if(!operation){
            return true;
        }
        var operations = [];
        if(typeof operation === 'string'){
            if(operation.indexOf(',') > -1){
                var sgt = operation.split(':');
                var ids = sgt[1].split(',');
                for(var i in ids){
                    operations.push(sgt[0]+':'+ids[i]);
                }
            }else{
                operations.push(operation);
            }
        }else{
            for(var i in operation){
                if(operation[i].indexOf(',') > -1){
                    var sgt = operation[i].split(':');
                    var ids = sgt[1].split(',');
                    for(var i in ids){
                        operations.push(sgt[0]+':'+ids[i]);
                    }
                }else{
                    operations.push(operation[i]);
                }
            }
            operations = operation;
        }
        var pass = false;
        for(var i in operations){
            var res = operations[i].split(':');
            if(res.length !== 2){
                continue;
            }
            var type = res[0];
            var code = res[1];
            if(code.length < 1){
                continue;
            }
            if(type === 'op' && this.operations.indexOf(code) > -1){
                pass = true;
            }else if(type === 'gp' && this.groups.indexOf(code) > -1){
                pass = true;
            }
        }
        return pass;
    }
};
//用户
App.user = {
    user: null,
    profileWin: null,
    resetPasswordWin: null,
    init: function(){
        var self = this;
        this.fushInfo();
        //修改资料
        this.profileWin = this.getProfileWin();
        $('.profile-edit-bnt').click(function(){
            self.profileWin.show();
            self.profileWin.form.change(self.user);
        });
        //修改密码
        this.resetPasswordWin = this.getResetPasswordWin();
        $('.password-reset-bnt').click(function(){
            self.resetPasswordWin.show();
            self.resetPasswordWin.form.reset();
        });
        //注销
        $('.logout-btn').click(function(){
            self.logout();
        });
    },
    fushInfo: function(){
        var self = this;
        App.util.request.getData({
            url: 'user',
            success: function(user){
                self.user = user;
                $('.user-img').attr('src', App.util.formator.fileUrl(user.icon, '/admin/img/user.jpg'));
                $('.user-nickname').text(user.nickname);
                $('.user-account').text(user.username);
                $('.user-role').text(user.role ? user.role.name : '无角色');
            }
        });
    },
    logout: function(){
        App.util.hint.confirm({
            text: '确定要退出登录吗？',
            yes: function(){
                App.util.request.postData({
                    url: 'logout',
                        success: function(){
                            App.util.hint.toast({
                            type: 'ok',
                            text: '注销成功，正在跳转...'
                        }).delay(500, function(){
                            window.location.href = 'login';
                        });
                    }
                });
            }
        });
    },
    getProfileWin: function(user){
        var self = this;
        var win = new App.Win({
            title: '用户中心 - 编辑资料',
            name: 'edit_profile',
            width: '500px',
            mini: false,
            lock: true,
            dragAble: false
        });
        //表单
        var form = new App.Form({items: [
            {type: 'input', name: 'nickname', title: '昵称', rule: {
                type: 'str',
                range: [1, 100]
            }},
            {type: 'img', name: 'icon', title: '头像', rule: {
                type: 'str',
                range: [0, 100]
            }},
            {type: 'input', name: 'name', title: '姓名', rule: {
                type: 'str',
                range: [0, 100]
            }},
            {type: 'input', name: 'phone', title: '手机', rule: {
                type: 'phone',
                range: [0, 100]
            }},
            {type: 'input', name: 'email', title: '邮箱', rule: {
                type: 'email',
                range: [0, 100]
            }},
            {type: 'textarea', name: 'introduction', title: '简介', rule: {
                type: 'str',
                range: [0, 250]
            }}
        ]});
        form.fix(win.contentContainer);
        win.form = form;
        //设置按钮
        win.setBtns([(function(){
            var cancelBtn = $('<a class="btn btn-danger btn-xs m-right-sm">取消</a>');
            cancelBtn.click(function(){
                win.hide();
            });
            return cancelBtn;
        })(), (function(){
            var saveBtn = $('<a class="btn btn-success btn-xs">保存</a>');
            saveBtn.click(function(){
                //数据验证
                if(!form.validate()){
                    return ;
                }
                //保存
                App.util.request.postData({
                    url: 'editProfile',
                    data: form.getData(),
                    loading: new App.Loading({container: win.contentContainer}),
                    success: function(){
                        win.hide();
                        App.util.hint.toast({
                            type: 'ok',
                            text: '密码修改成功！'
                        }).delay(500);
                        self.fushInfo();
                    }
                });
            });
            return saveBtn;
        })()]);
        win.hide();
        //安装
        win.fix(App.mainContainer);
        return win;
    },
    getResetPasswordWin: function(){
        var win = new App.Win({
            title: '用户中心 - 修改密码',
            name: 'reset_password',
            width: '350px',
            mini: false,
            lock: true,
            dragAble: false
        });
        //表单
        var form = new App.Form({items: [
            {type: 'password', name: 'password_old', title: '旧密码', rule: {
                type: 'str',
                range: [1, 100]
            }},
            {type: 'password', name: 'password_new', title: '新密码', rule: {
                type: 'str',
                range: [1, 100]
            }},
            {type: 'password', name: 'password_confirm', title: '确认密码', rule: {
                type: 'str',
                range: [1, 100]
            }}
        ]});
        form.fix(win.contentContainer);
        win.form = form;
        //设置按钮
        win.setBtns([(function(){
            var cancelBtn = $('<a class="btn btn-danger btn-xs m-right-sm">取消</a>');
            cancelBtn.click(function(){
                win.hide();
            });
            return cancelBtn;
        })(), (function(){
            var saveBtn = $('<a class="btn btn-success btn-xs">保存</a>');
            saveBtn.click(function(){
                //数据验证
                if(!form.validate()){
                    return ;
                }
                var data = form.getData();
                if(data.password_new !== data.password_confirm){
                    App.util.hint.toast({
                        type: 'warning',
                        text: '密码不一致'
                    }).delay(1000);
                    return ;
                }
                //保存
                App.util.request.postData({
                    url: 'resetPassword',
                    data: data,
                    loading: new App.Loading({container: win.contentContainer}),
                    success: function(){
                        win.hide();
                        App.util.hint.toast({
                            type: 'ok',
                            text: '密码修改成功！'
                        }).delay(500);
                    }
                });
            });
            return saveBtn;
        })()]);
        win.hide();
        //安装
        win.fix(App.mainContainer);
        return win;
    }
};
//主菜单
App.menu = {
    init: function(data){
        this.create(data);
        this.activeAble();
        this.pageAble();
    },
    create: function(data){
        var ul = $('.main-menu > ul');
        for(var i in data){//一级菜单
            var dataItem = data[i];
            if(!App.permission.check(dataItem['permission'])){
                continue;
            }
            var li = $('<li></li>');
            li.appendTo(ul);
            var a = $('<a href="javascript:void(0);"></a>');
            a.appendTo(li);
            if(dataItem['icon']){
                a.append($(dataItem['icon']));
            }
            a.append($('<span class="text">'+dataItem['name']+(dataItem['name-mini'] ? ' - '+dataItem['name-mini'] : '')+'</span>'));
            a.append('<span class="menu-hover"></span>');
            if(dataItem['openable']){
                li.addClass('openable');
                a.append($('<span class="menu-down"><i class="fa fa-caret-down"></i></span>'));
                a.append($('<span class="menu-up hidden"><i class="fa fa-caret-up"></i></span>'));
            }else{
                a.attr('data-url', dataItem['url']);
            }
            if(dataItem['items']){//二级菜单
                var ul2 = $('<ul class="submenu"></ul>');
                ul2.appendTo(li);
                for(var j in dataItem['items']){
                    var dataItem2 = dataItem['items'][j];
                    if(!App.permission.check(dataItem2['permission'])){
                        continue;
                    }
                    var li2 = $('<li></li>');
                    li2.appendTo(ul2);
                    var a = $('<a href="javascript:void(0);"></a>');
                    a.appendTo(li2);
                    a.append($('<span class="submenu-label">'+dataItem2['name']+'</span>'));
                    if(dataItem2['openable']){
                        li2.addClass('openable');
                        a.append($('<span class="menu-down"><i class="fa fa-caret-down"></i></span>'));
                        a.append($('<span class="menu-up hidden"><i class="fa fa-caret-up"></i></span>'));
                    }else{
                        a.attr('data-url', dataItem2['url']);
                    }
                    if(dataItem2['items']){//三级菜单
                        var ul3 = $('<ul class="submenu third-level"></ul>');
                        ul3.appendTo(li2);
                        for(var k in dataItem2['items']){
                            var dataItem3 = dataItem2['items'][k];
                            if(!App.permission.check(dataItem3['permission'])){
                                continue;
                            }
                            var li3 = $('<li></li>');
                            li3.appendTo(ul3);
                            var a = $('<a href="javascript:void(0);"></a>');
                            a.appendTo(li3);
                            a.append($('<span class="submenu-label">'+dataItem3['name']+'</span>'));
                            if(dataItem3['openable']){
                                li3.addClass('openable');
                            }else{
                                a.attr('data-url', dataItem3['url']);
                            }
                            if(dataItem3['items']){//四级菜单
                                var ul4 = $('<ul class="submenu fourth-level"></ul>');
                                ul4.appendTo(li3);
                                for(var l in dataItem3['items']){
                                    var dataItem4 = dataItem3['items'][l];
                                    if(!App.permission.check(dataItem4['permission'])){
                                        continue;
                                    }
                                    var li4 = $('<li></li>');
                                    li4.appendTo(ul4);
                                    var a = $('<a href="javascript:void(0);"></a>');
                                    a.appendTo(li4);
                                    a.append($('<span class="submenu-label">'+dataItem4['name']+'</span>'));
                                    a.attr('data-url', dataItem4['url']);
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    activeAble: function(){
        //Sidebar menu dropdown
        $('aside li').hover(
            function () {
                $(this).addClass('open');
            },
            function () {
                $(this).removeClass('open');
            }
        );

        //折叠
        $('.openable > a').click(function () {
            if(!$('#wrapper').hasClass('sidebar-mini')) {
                if($(this).parent().children('.submenu').is(':hidden')) {
                    $(this).parent().siblings().removeClass('open').children('.submenu').slideUp();
                    $(this).parent().siblings().each(function () {
                        $(this).find('.menu-up').addClass('hidden');
                        $(this).find('.menu-down').removeClass('hidden');
                    });
                    $(this).parent().addClass('open').children('.submenu').slideDown();
                    $(this).find('.menu-up').removeClass('hidden');
                    $(this).find('.menu-down').addClass('hidden');
                } else {
                    $(this).parent().removeClass('open').children('.submenu').slideUp();
                    $(this).find('.menu-up').addClass('hidden');
                    $(this).find('.menu-down').removeClass('hidden');
                }
            }
            return false;
        });

        //显示/隐藏菜单
        $('#menuToggle').click(function () {
            if($('#wrapper').hasClass('sm')){
                var btn = $(this);
                if(!$('#wrapper').hasClass('sidebar-display')){
                    $('#wrapper').addClass('sidebar-display');
                    App.domClickStack.add(function(event){
                        var menu = $('aside');
                        var onMenu = menu.is(event.target) || menu.has(event.target).length > 0;
                        var onSelf = btn.is(event.target) || btn.has(event.target).length > 0;
                        if(!onMenu && !onSelf){
                            $('#wrapper').removeClass('sidebar-display');
                            App.domClickStack.remove('main-menu-hide');
                        }
                    }, 'main-menu-hide');
                }else{
                    $('#wrapper').removeClass('sidebar-display');
                    App.domClickStack.remove('main-menu-hide');
                }
                return ;
            }
            $('.main-menu').find('.openable').removeClass('open');
            $('.main-menu').find('.submenu').removeAttr('style');
            $('#wrapper').toggleClass('sidebar-hide');
            App.resizeContainer();
        });
        $('#sizeToggle').click(function () {
            $('#wrapper').off("resize");
            $('#wrapper').toggleClass('sidebar-mini');
            $('.main-menu').find('.openable').removeClass('open');
            $('.main-menu').find('.submenu').removeAttr('style');
            $('.main-menu').find('.menu-up').addClass('hidden');
            $('.main-menu').find('.menu-down').removeClass('hidden');
            App.resizeContainer();
        });
        //mini菜单的悬浮
        $('.hover-dropdown').hover(
            function(){
                $(this).addClass('open');
            },
            function(){
                $(this).removeClass('open');
            }
        );
    },
    pageAble: function(){
        $('.main-menu a[data-url]').click(function(){
            $('.main-menu li').removeClass('active');
            if($(this).parent().parent().hasClass('fourth-level')){//四级菜单
                $(this).parent().parent().parent().parent().parent().parent().parent().addClass('active');
            }else if($(this).parent().parent().hasClass('third-level')){//三级菜单
                $(this).parent().parent().parent().parent().parent().addClass('active');
            }else if($(this).parent().parent().hasClass('submenu')){//二级菜单
                $(this).parent().parent().parent().addClass('active');
            }
            $(this).parent().addClass('active');
            App.nav.open($(this).attr('data-url'));
            if($('#wrapper').hasClass('sm')){
                $('#wrapper').removeClass('sidebar-display');
                App.domClickStack.remove('main-menu-hide');
            }
        });
    },
    change: function(name){
        var currentMenu = $('.main-menu a[data-url="'+name+'"]').first();
        if(currentMenu && !currentMenu.parent().hasClass('active')){
            this.openMenu(currentMenu.parent());
        }
    },
    openMenu: function(menu){
        $('.main-menu li').removeClass('active');
        if(menu.parent().hasClass('fourth-level')){//四级菜单
            menu.parent().parent().parent().parent().parent().parent().addClass('active');
        }else if(menu.parent().hasClass('third-level')){//三级菜单
            menu.parent().parent().parent().parent().addClass('active');
        }else if(menu.parent().hasClass('submenu')){//二级菜单
            menu.parent().parent().addClass('active');
        }
        menu.addClass('active');
    }
};
//用户
App.imgViewer = {
    imgs: [],
    frame: null,
    index: 0,
    win: null,
    init: function(){
        this.win = this.getWin();
        this.frame = this.getFrame();
        this.win.setContent(this.frame);
    },
    view: function(imgs){
        this.index = 0;
        this.imgs = imgs;
        this.flushImg();
        this.win.show();
    },
    getFrame: function(){
        var self = this;
        var frame = $([
            '<div style="width: 100%;height: 400px;position: relative;">',
            '    <img src="" style="max-height: 100%;max-width: 100%;"/>',
            '    <div class="img-viewer-left-btn" style="width: 50px;height: 100%;position: absolute;left: 0;top: 0;font-size: 25px;line-height: 400px;text-align: center;">',
            '        <i class="fa fa-chevron-left"></i>',
            '    </div>',
            '    <div class="img-viewer-right-btn" style="width: 50px;height: 100%;position: absolute;right: 0;top: 0;font-size: 25px;line-height: 400px;text-align: center;">',
            '        <i class="fa fa-chevron-right"></i>',
            '    </div>',
            '</div>'
        ].join(''));
        frame.find('.img-viewer-left-btn').click(function(){
            if(self.index < 1){
                return ;
            }
            self.index--;
            self.flushImg();
        });
        frame.find('.img-viewer-right-btn').click(function(){
            if(self.index >= self.imgs.length-1){
                return ;
            }
            self.index++;
            self.flushImg();
        });
        return frame;
    },
    flushImg: function(){
        this.frame.find('img').attr('src', this.imgs[this.index]);
        if(this.index < 1){
            this.frame.find('.img-viewer-left-btn').hide();
        }else{
            this.frame.find('.img-viewer-left-btn').show();
        }
        if(this.index >= this.imgs.length-1){
            this.frame.find('.img-viewer-right-btn').hide();
        }else{
            this.frame.find('.img-viewer-right-btn').show();
        }
        App.util.uploader.resizeImage(this.frame.find('img'), 600, 400);
    },
    getWin: function(){
        var win = new App.Win({
            title: '图片预览器',
            name: 'img_viewer',
            width: '598spx',
            mini: false,
            lock: true,
            dragAble: false
        });
        win.hide();
        //安装
        win.fix(App.mainContainer);
        return win;
    }
};
